]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
libblkid: iso9660: allocate enough space for UTF16 decoding
authorThomas Weißschuh <thomas@t-8ch.de>
Thu, 10 Nov 2022 18:48:20 +0000 (19:48 +0100)
committerThomas Weißschuh <thomas@t-8ch.de>
Tue, 15 Nov 2022 16:43:41 +0000 (17:43 +0100)
When merge_utf16be_ascii() encounters high-codepoint surrogate pairs it
emits four bytes of output for one byte of ascii input.
In addition with the remaining ascii characters from the second loop we
need up to 5 * sizeof(input) / 2 bytes as output buffer.

As we decode up to 128 ascii characters with merge_utf16be_ascii() we
need 320 bytes of buffer available.

Furthermore adapt merge_utf16be_ascii() to not write paste the output
buffer end.

Fix OSS-Fuzz issue 53149 ( #1887 )

libblkid/src/superblocks/iso9660.c
tests/ts/fuzzers/test_blkid_fuzz_files/oss-fuzz-53149 [new file with mode: 0644]

index 503126c0060bad411c37f0af6031ded8872e4217..1172f9c259b127f6a86f8cff4c70f985be90b4c2 100644 (file)
@@ -70,6 +70,8 @@ struct boot_record {
 #define ISO_VD_SUPPLEMENTARY           0x2
 #define ISO_VD_END                     0xff
 #define ISO_VD_MAX                     16
+/* maximal string field size used anywhere in ISO; update if necessary */
+#define ISO_MAX_FIELDSIZ  sizeof(((struct iso_volume_descriptor  *)0)->volume_set_id)
 
 struct high_sierra_volume_descriptor {
        unsigned char   foo[8];
@@ -169,11 +171,11 @@ static int is_utf16be_str_empty(unsigned char *utf16, size_t len)
 /* if @utf16 is prefix of @ascii (ignoring non-representable characters and upper-case conversion)
  * then reconstruct prefix from @utf16 and @ascii, append suffix from @ascii, fill it into @out
  * and returns length of bytes written into @out; otherwise returns zero */
-static size_t merge_utf16be_ascii(unsigned char *out, const unsigned char *utf16, const unsigned char *ascii, size_t len)
+static size_t merge_utf16be_ascii(unsigned char *out, size_t out_len, const unsigned char *utf16, const unsigned char *ascii, size_t len)
 {
        size_t o, a, u;
 
-       for (o = 0, a = 0, u = 0; u + 1 < len && a < len; o += 2, a++, u += 2) {
+       for (o = 0, a = 0, u = 0; u + 1 < len && a < len && o + 1 < out_len; o += 2, a++, u += 2) {
                /* Surrogate pair with code point above U+FFFF */
                if (utf16[u] >= 0xD8 && utf16[u] <= 0xDB && u + 3 < len &&
                    utf16[u + 2] >= 0xDC && utf16[u + 2] <= 0xDF) {
@@ -195,7 +197,7 @@ static size_t merge_utf16be_ascii(unsigned char *out, const unsigned char *utf16
                }
        }
 
-       for (; a < len; o += 2, a++) {
+       for (; a < len && o + 1 < out_len; o += 2, a++) {
                out[o] = 0x00;
                out[o + 1] = ascii[a];
        }
@@ -209,7 +211,8 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
        struct boot_record *boot = NULL;
        struct iso_volume_descriptor *pvd = NULL;
        struct iso_volume_descriptor *joliet = NULL;
-       unsigned char buf[256];
+       /* space for merge_utf16be_ascii(ISO_ID_BUFSIZ bytes) */
+       unsigned char buf[ISO_MAX_FIELDSIZ * 5 / 2];
        size_t len;
        int is_unicode_empty;
        int is_ascii_empty;
@@ -253,14 +256,14 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
        blkid_probe_set_fsblocksize(pr, ISO_SECTOR_SIZE);
        blkid_probe_set_block_size(pr, ISO_SECTOR_SIZE);
 
-       if (joliet && (len = merge_utf16be_ascii(buf, joliet->system_id, pvd->system_id, sizeof(pvd->system_id))) != 0)
+       if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->system_id, pvd->system_id, sizeof(pvd->system_id))) != 0)
                blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", buf, len, UL_ENCODE_UTF16BE);
        else if (joliet)
                blkid_probe_set_utf8_id_label(pr, "SYSTEM_ID", joliet->system_id, sizeof(joliet->system_id), UL_ENCODE_UTF16BE);
        else
                blkid_probe_set_id_label(pr, "SYSTEM_ID", pvd->system_id, sizeof(pvd->system_id));
 
-       if (joliet && (len = merge_utf16be_ascii(buf, joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0)
+       if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_set_id, pvd->volume_set_id, sizeof(pvd->volume_set_id))) != 0)
                blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", buf, len, UL_ENCODE_UTF16BE);
        else if (joliet)
                blkid_probe_set_utf8_id_label(pr, "VOLUME_SET_ID", joliet->volume_set_id, sizeof(joliet->volume_set_id), UL_ENCODE_UTF16BE);
@@ -269,7 +272,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
 
        is_ascii_empty = (is_str_empty(pvd->publisher_id, sizeof(pvd->publisher_id)) || pvd->publisher_id[0] == '_');
        is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->publisher_id, sizeof(joliet->publisher_id)) || (joliet->publisher_id[0] == 0x00 && joliet->publisher_id[1] == '_'));
-       if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0)
+       if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->publisher_id, pvd->publisher_id, sizeof(pvd->publisher_id))) != 0)
                blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", buf, len, UL_ENCODE_UTF16BE);
        else if (!is_unicode_empty)
                blkid_probe_set_utf8_id_label(pr, "PUBLISHER_ID", joliet->publisher_id, sizeof(joliet->publisher_id), UL_ENCODE_UTF16BE);
@@ -278,7 +281,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
 
        is_ascii_empty = (is_str_empty(pvd->data_preparer_id, sizeof(pvd->data_preparer_id)) || pvd->data_preparer_id[0] == '_');
        is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->data_preparer_id, sizeof(joliet->data_preparer_id)) || (joliet->data_preparer_id[0] == 0x00 && joliet->data_preparer_id[1] == '_'));
-       if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0)
+       if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->data_preparer_id, pvd->data_preparer_id, sizeof(pvd->data_preparer_id))) != 0)
                blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", buf, len, UL_ENCODE_UTF16BE);
        else if (!is_unicode_empty)
                blkid_probe_set_utf8_id_label(pr, "DATA_PREPARER_ID", joliet->data_preparer_id, sizeof(joliet->data_preparer_id), UL_ENCODE_UTF16BE);
@@ -287,7 +290,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
 
        is_ascii_empty = (is_str_empty(pvd->application_id, sizeof(pvd->application_id)) || pvd->application_id[0] == '_');
        is_unicode_empty = (!joliet || is_utf16be_str_empty(joliet->application_id, sizeof(joliet->application_id)) || (joliet->application_id[0] == 0x00 && joliet->application_id[1] == '_'));
-       if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0)
+       if (!is_unicode_empty && !is_ascii_empty && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->application_id, pvd->application_id, sizeof(pvd->application_id))) != 0)
                blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", buf, len, UL_ENCODE_UTF16BE);
        else if (!is_unicode_empty)
                blkid_probe_set_utf8_id_label(pr, "APPLICATION_ID", joliet->application_id, sizeof(joliet->application_id), UL_ENCODE_UTF16BE);
@@ -312,7 +315,7 @@ static int probe_iso9660(blkid_probe pr, const struct blkid_idmag *mag)
         * version of label in PVD. Based on these facts try to reconstruct original label if label
         * in Joliet is prefix of the label in PVD (ignoring non-representable characters).
         */
-       if (joliet && (len = merge_utf16be_ascii(buf, joliet->volume_id, pvd->volume_id, sizeof(pvd->volume_id))) != 0)
+       if (joliet && (len = merge_utf16be_ascii(buf, sizeof(buf), joliet->volume_id, pvd->volume_id, sizeof(pvd->volume_id))) != 0)
                blkid_probe_set_utf8label(pr, buf, len, UL_ENCODE_UTF16BE);
        else if (joliet)
                blkid_probe_set_utf8label(pr, joliet->volume_id, sizeof(joliet->volume_id), UL_ENCODE_UTF16BE);
diff --git a/tests/ts/fuzzers/test_blkid_fuzz_files/oss-fuzz-53149 b/tests/ts/fuzzers/test_blkid_fuzz_files/oss-fuzz-53149
new file mode 100644 (file)
index 0000000..f1c6c3c
Binary files /dev/null and b/tests/ts/fuzzers/test_blkid_fuzz_files/oss-fuzz-53149 differ