]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
[Zip] Reject entries with overlong bodies 3230/head
authorTim Kientzle <kientzle@acm.org>
Fri, 3 Jul 2026 20:14:27 +0000 (13:14 -0700)
committerTim Kientzle <kientzle@acm.org>
Fri, 3 Jul 2026 20:14:27 +0000 (13:14 -0700)
If an entry declares an uncompressed size, we should not return any
data beyond that point.  This doesn't impact bsdtar and friends since
they independently truncate to the declared entry size, but other
clients may not be so careful.

Apparently discovered (but not reported) by @bikini
Reported by @dkgkdfg65

libarchive/archive_read_support_format_zip.c

index e685f85e55ee2b7db7654389e3c294d7e035102e..2ce62e804e5889920f3c711fff7866c6a3962db8 100644 (file)
@@ -259,6 +259,15 @@ struct zip {
 /* Many systems define min or MIN, but not all. */
 #define        zipmin(a,b) ((a) < (b) ? (a) : (b))
 
+/* True if this entry has declared a specific uncompressed size. */
+static int
+zip_entry_size_is_set(const struct zip_entry *zip_entry)
+{
+       return (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
+           || (zip_entry->uncompressed_size > 0
+               && zip_entry->uncompressed_size != 0xffffffff));
+}
+
 #ifdef HAVE_ZLIB_H
 static int
 zip_read_data_deflate(struct archive_read *a, const void **buff,
@@ -1435,10 +1444,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
                            "Read error skipping symlink target name");
                        return ARCHIVE_FATAL;
                }
-       } else if (0 == (zip_entry->zip_flags & ZIP_LENGTH_AT_END)
-          || (zip_entry->uncompressed_size > 0
-              && zip_entry->uncompressed_size != 0xffffffff)) {
-               /* Set the size only if it's meaningful. */
+       } else if (zip_entry_size_is_set(zip_entry)) {
                archive_entry_set_size(entry, zip_entry->uncompressed_size);
        }
        zip->entry_bytes_remaining = zip_entry->compressed_size;
@@ -3426,6 +3432,25 @@ archive_read_format_zip_read_data(struct archive_read *a,
        }
        if (r != ARCHIVE_OK)
                return (r);
+
+       /*
+        * FAIL if there are more uncompressed bytes than were
+        * initially advertised.  The end-of-entry check below also
+        * compares these values, but only once decoding reaches its
+        * own natural end. Fail the entry so a caller relying on
+        * archive_entry_size() as a hard boundary is never misled;
+        * later entries in the archive can still be read normally.
+        */
+       if (*size > 0 && zip_entry_size_is_set(zip->entry) &&
+           zip->entry_uncompressed_bytes_read > zip->entry->uncompressed_size) {
+               archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+                   "ZIP uncompressed data is larger than the declared "
+                   "entry size (read at least %jd, expected %jd)",
+                   (intmax_t)zip->entry_uncompressed_bytes_read,
+                   (intmax_t)zip->entry->uncompressed_size);
+               return (ARCHIVE_FAILED);
+       }
+
        if (*size > 0) {
                zip->computed_crc32 = zip->crc32func(zip->computed_crc32, *buff,
                                                     (unsigned)*size);