From: Tim Kientzle Date: Fri, 3 Jul 2026 20:14:27 +0000 (-0700) Subject: [Zip] Reject entries with overlong bodies X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c11df428d134c2291019dd02db44f33b4435bde2;p=thirdparty%2Flibarchive.git [Zip] Reject entries with overlong bodies 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 --- diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index e685f85e5..2ce62e804 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -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);