From: Russell Mullens Date: Tue, 23 Mar 2021 11:22:07 +0000 (+1100) Subject: Fix excessive disk read for padded zip. X-Git-Tag: v3.5.2~25^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=03dd1e54c08715364141e0aa091151ea8ee4f5a4;p=thirdparty%2Flibarchive.git Fix excessive disk read for padded zip. Use the size of the Central Directory and the offset of the EOCD to calculate the real position. This trick doesn't work for Zip64 as easily as we are not scanning backwards to find the PK\x06\x06 entry. Interestingly, it is never checked so it could be trying to parse bad files. Updated based on review --- diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index d7f318f80..26291237c 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -142,7 +142,7 @@ struct zip { /* Structural information about the archive. */ struct archive_string format_name; int64_t central_directory_offset; - int64_t central_directory_offset_actual; + int64_t central_directory_offset_adjusted; size_t central_directory_entries_total; size_t central_directory_entries_on_this_disk; int has_encrypted_entries; @@ -3416,26 +3416,28 @@ archive_read_support_format_zip_capabilities_seekable(struct archive_read * a) static int read_eocd(struct zip *zip, const char *p, int64_t current_offset) { + uint16_t disk_num = archive_le16dec(p + 4); + uint32_t cd_size = archive_le32dec(p + 12); + uint32_t cd_offset = archive_le32dec(p + 16); + /* Sanity-check the EOCD we've found. */ /* This must be the first volume. */ - if (archive_le16dec(p + 4) != 0) + if (disk_num != 0) return 0; /* Central directory must be on this volume. */ - if (archive_le16dec(p + 4) != archive_le16dec(p + 6)) + if (disk_num != archive_le16dec(p + 6)) return 0; /* All central directory entries must be on this volume. */ if (archive_le16dec(p + 10) != archive_le16dec(p + 8)) return 0; /* Central directory can't extend beyond start of EOCD record. */ - if (archive_le32dec(p + 16) + archive_le32dec(p + 12) - > current_offset) + if (cd_offset + cd_size > current_offset) return 0; /* Save the central directory location for later use. */ - zip->central_directory_offset = archive_le32dec(p + 16); - zip->central_directory_offset_actual = current_offset - - archive_le32dec(p+12) - 1; + zip->central_directory_offset = cd_offset; + zip->central_directory_offset_adjusted = current_offset - cd_size; /* This is just a tiny bit higher than the maximum returned by the streaming Zip bidder. This ensures @@ -3487,7 +3489,8 @@ read_zip64_eocd(struct archive_read *a, struct zip *zip, const char *p) /* Save the central directory offset for later use. */ zip->central_directory_offset = archive_le64dec(p + 48); - zip->central_directory_offset_actual = zip->central_directory_offset; + /* TODO: Needs scanning backwards to find the eocd64 instead of assuming */ + zip->central_directory_offset_adjusted = zip->central_directory_offset; return 32; } @@ -3659,7 +3662,7 @@ slurp_central_directory(struct archive_read *a, struct archive_entry* entry, * know the correction we need to apply to account for leading * padding. */ - if (__archive_read_seek(a, zip->central_directory_offset_actual, SEEK_SET) + if (__archive_read_seek(a, zip->central_directory_offset_adjusted, SEEK_SET) < 0) return ARCHIVE_FATAL;