Index: common/stdc/uchar.c
===================================================================
--- common/stdc/uchar.c	(revision 163e34ce66d8952017bc5a812affc2f72443b230)
+++ common/stdc/uchar.c	(revision 06009762b7fb89c5261e22ebc8a890bf71867b44)
@@ -84,4 +84,10 @@
 }
 
+static bool _is_non_shortest(unsigned short cont, uint8_t b)
+{
+	return (cont == 0b1111110000000000 && !(b & 0b00100000)) ||
+	    (cont == 0b1111111111110000 && !(b & 0b00110000));
+}
+
 size_t mbrtoc32(char32_t *c, const char *s, size_t n, mbstate_t *mb)
 {
@@ -139,4 +145,10 @@
 
 		if (_is_2_byte(b)) {
+			/* Reject non-shortest form. */
+			if (!(b & 0b00011110)) {
+				_set_ilseq();
+				return UCHAR_ILSEQ;
+			}
+
 			/* 2 byte encoding               110xxxxx */
 			mb->continuation = b ^ 0b0000000011000000;
@@ -152,8 +164,9 @@
 	}
 
-	while (i < n) {
+	for (; i < n; i++) {
 		/* Read continuation bytes. */
-
-		if (!_is_continuation(s[i])) {
+		uint8_t b = s[i];
+
+		if (!_is_continuation(b) || _is_non_shortest(mb->continuation, b)) {
 			_set_ilseq();
 			return UCHAR_ILSEQ;
@@ -162,10 +175,10 @@
 		/* Top bit becomes zero just before the last byte is shifted in. */
 		if (!(mb->continuation & 0x8000)) {
-			*c = ((char32_t) mb->continuation) << 6 | (s[i++] & 0x3f);
+			*c = ((char32_t) mb->continuation) << 6 | (b & 0x3f);
 			mb->continuation = 0;
-			return i;
-		}
-
-		mb->continuation = mb->continuation << 6 | (s[i++] & 0x3f);
+			return ++i;
+		}
+
+		mb->continuation = mb->continuation << 6 | (b & 0x3f);
 	}
 
