]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix excessive disk read for padded zip.
authorRussell Mullens <the_pimaster@hotmail.com>
Tue, 23 Mar 2021 11:22:07 +0000 (22:22 +1100)
committerRussell Mullens <the_pimaster@hotmail.com>
Tue, 23 Mar 2021 11:22:07 +0000 (22:22 +1100)
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

libarchive/archive_read_support_format_zip.c

index d7f318f80aa0dd37182c5d931ff12ee72def8b62..26291237cde99e984e156e7793f718ad7e76a1c8 100644 (file)
@@ -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;