]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix Heap OOB Write in CAB LZX decoder
authorLoboQ1ng <xpess@qq.com>
Wed, 18 Mar 2026 00:24:40 +0000 (00:24 +0000)
committerLoboQ1ng <xpess@qq.com>
Wed, 18 Mar 2026 00:24:40 +0000 (00:24 +0000)
libarchive/archive_read_support_format_cab.c
libarchive/test/CMakeLists.txt
libarchive/test/test_read_format_cab_lzx_oob.c [new file with mode: 0644]
libarchive/test/test_read_format_cab_lzx_oob.cab.uu [new file with mode: 0644]

index 8b6e8370cab602801f30f084d2904103d9e0a821..3951ee91d4a0429087eeefa2be9ba161a6673024 100644 (file)
@@ -1688,6 +1688,13 @@ cab_read_ahead_cfdata_lzx(struct archive_read *a, ssize_t *avail)
                    cab->uncompressed_buffer + cab->xstrm.total_out;
                cab->xstrm.avail_out =
                    cfdata->uncompressed_size - cab->xstrm.total_out;
+               
+               if ((size_t)cfdata->uncompressed_size > cab->uncompressed_buffer_size) {
+                       archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                               "Invalid CFDATA uncompressed size");
+                       *avail = ARCHIVE_FATAL;
+                       return (NULL);
+               }
 
                d = __archive_read_ahead(a, 1, &bytes_avail);
                if (d == NULL) {
index 6f51e3c5f6b968c59a7dbf606d0d54821b6fbe00..cd5416d554133de59295af408f38fb1220c58b9f 100644 (file)
@@ -114,6 +114,7 @@ IF(ENABLE_TEST)
     test_read_format_ar.c
     test_read_format_cab.c
     test_read_format_cab_filename.c
+    test_read_format_cab_lzx_oob.c
     test_read_format_cab_skip_malformed.c
     test_read_format_cpio_afio.c
     test_read_format_cpio_bin.c
diff --git a/libarchive/test/test_read_format_cab_lzx_oob.c b/libarchive/test/test_read_format_cab_lzx_oob.c
new file mode 100644 (file)
index 0000000..14c89b8
--- /dev/null
@@ -0,0 +1,49 @@
+#include "test.h"
+
+/*
+ * Regression test for Heap Out-of-Bounds Write in CAB LZX decoder.
+ * This ensures that a malformed CFDATA uncompressed size does not
+ * bypass physical buffer limits and cause memory corruption during skips.
+ */
+DEFINE_TEST(test_read_format_cab_lzx_oob)
+{
+       const char *refname = "test_read_format_cab_lzx_oob.cab";
+       struct archive *a;
+       struct archive_entry *ae;
+       const void *buff;
+       size_t size;
+       int64_t offset;
+
+       /* * The test framework will automatically find 'test_read_format_cab_lzx_oob.cab.uu',
+        * decode it, and place the binary '.cab' in the temporary test directory.
+        */
+       extract_reference_file(refname);
+
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_cab(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
+
+       /* If it fails to open, there's a problem with the test setup/file */
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 10240));
+
+       /* Read the header of the malformed entry */
+       if (ARCHIVE_OK == archive_read_next_header(a, &ae)) {
+               /* * We do NOT assert ARCHIVE_OK here. The file is intentionally malformed.
+                * The goal is to ensure the patched decoder catches the malicious size
+                * and returns an error (ARCHIVE_FATAL or ARCHIVE_WARN) instead of crashing.
+                */
+               if (archive_read_data_block(a, &buff, &size, &offset) == ARCHIVE_OK) {
+                       archive_read_data_skip(a);
+               } else {
+                       /* Even if the first block read fails, force a skip to test state handling */
+                       archive_read_data_skip(a);
+               }
+               
+               /* * Optional: We could assert that the error string contains our patch message, 
+                * but simply surviving without a segfault/ASAN violation is the primary goal 
+                * for fuzzing regression tests.
+                */
+       }
+
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+}
\ No newline at end of file
diff --git a/libarchive/test/test_read_format_cab_lzx_oob.cab.uu b/libarchive/test/test_read_format_cab_lzx_oob.cab.uu
new file mode 100644 (file)
index 0000000..2f453ef
--- /dev/null
@@ -0,0 +1,11 @@
+begin 664 test_read_format_cab_lzx_oob.cab
+M35-#1@````!!``"2DI*2DI(````````2Y``!``$``,2PW@``'P$```$`0Q`!
+M`3O2T@D)"0D)"0D)``````````````"RLK*RLK*RLK*RLK*RLK*RLK(*,```
+M````````````````LK*2X____[:RL@``````````LK*R"C``````````````
+M`````+*RDN/___^VLK(```````````"R'YV)3``````````````0$!`0$!`0
+M$!`0$!D0$!`0$!`0$!`0$!`0$!`0$!#___\/$!`0`````````$`0$`!`,#<P
+M-S`R````,#82SWXO+R\`_P``````````````````````````````````````
+M``````#U\DH*-S`R````,#82SWXO+R\`_P``````````````````````````
+.````````````````````
+`
+end