]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Detect UTF-16 surrogates in `isc_utf8_valid()`
authorColin Vidal <colin@isc.org>
Thu, 2 Jul 2026 07:52:09 +0000 (09:52 +0200)
committerColin Vidal <colin@isc.org>
Wed, 8 Jul 2026 08:31:10 +0000 (10:31 +0200)
UTF-8 standard forbid usage of unicode character between the range of
0xD800..0xDFFF (reserved, and used as UTF-16 surrogates, see RFC 3629).

However, `usc_utf8_valid()` was not checking if the encoded unicode
character was in this range, which then would accept invalid UTF-8
strings. This is now fixed.

lib/isc/utf8.c

index 9bc2a74fb9483a4893e75aa6a4afe5db09d40a39..fb78338421961a898e763a49a092a82e34ba0985 100644 (file)
@@ -33,9 +33,20 @@ isc_utf8_valid(const unsigned char *buf, size_t len) {
        REQUIRE(buf != NULL);
 
        for (size_t i = 0; i < len; i++) {
+               /*
+                * ASCII character range (first row).
+                */
                if (buf[i] <= 0x7f) {
                        continue;
                }
+
+               /*
+                * 0x80 -> 1000 0000
+                * 0xC0 -> 1100 0000
+                * 0xE0 -> 1110 0000
+                *
+                * Is unicode character is encoded using 2 bytes (second row).
+                */
                if ((i + 1) < len && (buf[i] & 0xe0) == 0xc0 &&
                    (buf[i + 1] & 0xc0) == 0x80)
                {
@@ -47,6 +58,15 @@ isc_utf8_valid(const unsigned char *buf, size_t len) {
                        }
                        continue;
                }
+
+               /*
+                * 0x80 -> 1000 0000
+                * 0xC0 -> 1100 0000
+                * 0xE0 -> 1110 0000
+                * 0xF0 -> 1111 0000
+                *
+                * Is unicode character is encoded within 3 bytes (third row).
+                */
                if ((i + 2) < len && (buf[i] & 0xf0) == 0xe0 &&
                    (buf[i + 1] & 0xc0) == 0x80 && (buf[i + 2] & 0xc0) == 0x80)
                {
@@ -57,8 +77,26 @@ isc_utf8_valid(const unsigned char *buf, size_t len) {
                        if (w < 0x0800) {
                                return false;
                        }
+
+                       /*
+                        * Unicode range 0xD800..0xDFFF is reserved (UTF16
+                        * surrogates)
+                        */
+                       if (w >= 0xD800 && w <= 0xDFFF) {
+                               return false;
+                       }
                        continue;
                }
+
+               /*
+                * 0x80 -> 1000 0000
+                * 0xC0 -> 1100 0000
+                * 0xE0 -> 1110 0000
+                * 0xF0 -> 1111 0000
+                * 0xF8 -> 1111 1000
+                *
+                * Is unicode character is encoded within 4 bytes (fourth row).
+                */
                if ((i + 3) < len && (buf[i] & 0xf8) == 0xf0 &&
                    (buf[i + 1] & 0xc0) == 0x80 &&
                    (buf[i + 2] & 0xc0) == 0x80 && (buf[i + 3] & 0xc0) == 0x80)