]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
ZIP reader: fixed an unlimited loop in BZIP2 decompressor. 1153/head
authorGrzegorz Antoniak <ga@anadoxin.org>
Thu, 28 Feb 2019 06:26:25 +0000 (07:26 +0100)
committerGrzegorz Antoniak <ga@anadoxin.org>
Fri, 1 Mar 2019 08:26:51 +0000 (09:26 +0100)
The hang was triggered on invalid zipx files that declare more data than
there actually is in the file.

This case was found by fuzzing the zipx file.

libarchive/archive_read_support_format_zip.c
libarchive/test/test_read_format_zip.c
libarchive/test/test_read_format_zip_bz2_hang.zip.uu [new file with mode: 0644]

index 723ea1e04c3e46c8f711e39dd6b0d846582b0f37..677c4370954803f45a5ff3be95cecadbcbb9cb16 100644 (file)
@@ -1984,6 +1984,15 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
        }
 
        in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+       if(in_bytes < 1) {
+               /* libbz2 doesn't complain when caller feeds avail_in == 0. It will
+                * actually return success in this case, which is undesirable. This is
+                * why we need to make this check manually. */
+
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+                   "Truncated bzip2 file body");
+               return (ARCHIVE_FATAL);
+       }
 
        /* Setup buffer boundaries. */
        zip->bzstream.next_in = (char*)(uintptr_t) compressed_buff;
index bc04933c3ea320e9eca57e5be596250ce7ece086..14c5ada81053db2987dac3b481f7ddd228534709 100644 (file)
@@ -769,6 +769,28 @@ DEFINE_TEST(test_read_format_zip_ppmd8_crash_1)
 
        extract_reference_file(refname);
 
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 100));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+
+       /* This file shouldn't be properly decompressed, because it's invalid.
+        * However, unpacker should return an error during unpacking. Without the
+        * proper fix, the unpacker was entering an unlimited loop. */
+       assertEqualIntA(a, ARCHIVE_FATAL, archive_read_data(a, buf, 1));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a));
+}
+
+DEFINE_TEST(test_read_format_zip_bz2_hang_on_invalid)
+{
+       const char *refname = "test_read_format_zip_bz2_hang.zip";
+       struct archive *a;
+       struct archive_entry *ae;
+       char buf[8];
+
+       extract_reference_file(refname);
+
        assert((a = archive_read_new()) != NULL);
        assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip(a));
        assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, refname, 37));
diff --git a/libarchive/test/test_read_format_zip_bz2_hang.zip.uu b/libarchive/test/test_read_format_zip_bz2_hang.zip.uu
new file mode 100644 (file)
index 0000000..5193a77
--- /dev/null
@@ -0,0 +1,5 @@
+begin 644 test_read_format_zip_bz2_hang.zip
+M4$L#!)LP,#`,,#`P,#`P,#`P,#`P,#`P,#`$`!P`,#`P,#`P"0`P,#`P,#`P
+1,#!U>`L`8(0P,#`P,#`P,#``
+`
+end