From: Tim Kientzle Date: Sun, 24 Nov 2013 00:01:11 +0000 (-0800) Subject: Add tests to verify that the seeking Zip reader can correctly X-Git-Tag: v3.1.900a~361^2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=37908ea3c9731a34c9a6216001fddbcf73ca5940;p=thirdparty%2Flibarchive.git Add tests to verify that the seeking Zip reader can correctly ignore padding at beginning or end. Fix Issue #257. --- diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c index c9fae7f24..e6b9c7690 100644 --- a/libarchive/archive_read_support_format_zip.c +++ b/libarchive/archive_read_support_format_zip.c @@ -245,7 +245,7 @@ static int archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) { struct zip *zip = (struct zip *)a->format->data; - int64_t filesize; + int64_t end_of_central_directory_offset; const char *p; /* If someone has already bid more than 32, then avoid @@ -253,46 +253,46 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) if (best_bid > 32) return (-1); - filesize = __archive_read_seek(a, -22, SEEK_END); + /* end-of-central-directory record is 22 bytes; first check + * for it at very end of file. */ + end_of_central_directory_offset = __archive_read_seek(a, -22, SEEK_END); /* If we can't seek, then we can't bid. */ - if (filesize <= 0) + if (end_of_central_directory_offset <= 0) return 0; - /* TODO: More robust search for end of central directory record. */ if ((p = __archive_read_ahead(a, 22, NULL)) == NULL) return 0; /* First four bytes are signature for end of central directory - record. Four zero bytes ensure this isn't a multi-volume - Zip file (which we don't yet support). */ + * record. Four zero bytes ensure this isn't a multi-volume + * Zip file (which we don't yet support). */ if (memcmp(p, "PK\005\006\000\000\000\000", 8) != 0) { int64_t i, tail; int found; /* - * If there is a comment in end of central directory - * record, 22 bytes are too short. we have to read more - * to properly detect the record. Hopefully, a length - * of the comment is not longer than 16362 bytes(16K-22). + * End-of-central-directory isn't exactly at end of file. + * Try reading last 16k of file and search for it. */ - if (filesize + 22 > 1024 * 16) { + if (end_of_central_directory_offset + 22 > 1024 * 16) { tail = 1024 * 16; - filesize = __archive_read_seek(a, tail * -1, SEEK_END); + end_of_central_directory_offset + = __archive_read_seek(a, tail * -1, SEEK_END); } else { - tail = filesize + 22; - filesize = __archive_read_seek(a, 0, SEEK_SET); + tail = end_of_central_directory_offset + 22; + end_of_central_directory_offset + = __archive_read_seek(a, 0, SEEK_SET); } - if (filesize < 0) + if (end_of_central_directory_offset < 0) return 0; if ((p = __archive_read_ahead(a, (size_t)tail, NULL)) == NULL) return 0; - for (found = 0, i = 0;!found && i < tail - 22;) { + for (found = 0, i = 0; !found && i < tail - 22;) { switch (p[i]) { case 'P': if (memcmp(p+i, "PK\005\006\000\000\000\000", 8) == 0) { p += i; - filesize += tail - - (22 + archive_le16dec(p+20)); + end_of_central_directory_offset += i; found = 1; } else i += 8; @@ -313,14 +313,14 @@ archive_read_format_zip_seekable_bid(struct archive_read *a, int best_bid) zip->central_directory_entries = archive_le16dec(p + 10); zip->central_directory_size = archive_le32dec(p + 12); zip->central_directory_offset = archive_le32dec(p + 16); - zip->end_of_central_directory_offset = filesize; + zip->end_of_central_directory_offset = end_of_central_directory_offset; /* Just one volume, so central dir must all be on this volume. */ if (zip->central_directory_entries != archive_le16dec(p + 8)) return 0; - /* Central directory can't extend beyond end of this file. */ + /* Central directory can't extend beyond start of EOCD record. */ if (zip->central_directory_offset + - (int64_t)zip->central_directory_size > filesize) + (int64_t)zip->central_directory_size > end_of_central_directory_offset) return 0; /* This is just a tiny bit higher than the maximum returned by diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt index dadea4c69..560e7ff4c 100644 --- a/libarchive/test/CMakeLists.txt +++ b/libarchive/test/CMakeLists.txt @@ -143,6 +143,7 @@ IF(ENABLE_TEST) test_read_format_zip_comment_stored.c test_read_format_zip_filename.c test_read_format_zip_mac_metadata.c + test_read_format_zip_padded.c test_read_format_zip_sfx.c test_read_large.c test_read_pax_truncated.c diff --git a/libarchive/test/test_read_format_zip_padded.c b/libarchive/test/test_read_format_zip_padded.c new file mode 100644 index 000000000..dae88abba --- /dev/null +++ b/libarchive/test/test_read_format_zip_padded.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2013 Tim Kientzle + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "test.h" +__FBSDID("$FreeBSD$"); + +static void +verify_padded_archive(const char *refname) +{ + char *p; + size_t s; + struct archive *a; + struct archive_entry *ae; + + extract_reference_file(refname); + p = slurpfile(&s, refname); + + assert((a = archive_read_new()) != NULL); + assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_zip_seekable(a)); + assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, p, s, 1)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("file0", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae)); + assertEqualInt(6, archive_entry_size(ae)); + + assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); + assertEqualString("file1", archive_entry_pathname(ae)); + assertEqualInt(AE_IFREG | 0644, archive_entry_mode(ae)); + assertEqualInt(6, archive_entry_size(ae)); + + assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); + assertEqualIntA(a, ARCHIVE_OK, archive_read_free(a)); +} + +/* + * Read a zip file with padding in front. + * This is technically a malformed file, since the + * internal file offsets aren't adjusted to account + * for the added padding. Unfortunately, some SFX + * creators do almost exactly this: + */ +DEFINE_TEST(test_read_format_zip_padded1) +{ + const char *refname = "test_read_format_zip_padded1.zip"; + verify_padded_archive(refname); +} + +/* + * Read a zip file with padding at end. + * Small amounts of end padding should just be ignored by the reader. + * (If there's too much, the reader won't find the central directory.) + */ +DEFINE_TEST(test_read_format_zip_padded2) +{ + const char *refname = "test_read_format_zip_padded2.zip"; + verify_padded_archive(refname); +} + +/* + * Read a zip file with padding at front and end. + */ +DEFINE_TEST(test_read_format_zip_padded3) +{ + const char *refname = "test_read_format_zip_padded3.zip"; + verify_padded_archive(refname); +} + diff --git a/libarchive/test/test_read_format_zip_padded1.zip.uu b/libarchive/test/test_read_format_zip_padded1.zip.uu new file mode 100644 index 000000000..8cc3f4128 --- /dev/null +++ b/libarchive/test/test_read_format_zip_padded1.zip.uu @@ -0,0 +1,12 @@ +begin 644 test_read_format_zip_padded1.zip +MBG[*5O,EN^?);45X-!:9?HM14B$W +MD5)U>`L``03U`0``!!0```!F:6QE,`I02P,$"@``````^'EW0P3W*>(&```` +M!@````4`'`!F:6QE,554"0`#)#>14B0WD5)U>`L``03U`0``!!0```!F:6QE +M,0I02P$"'@,*``````#W>7=#1<8R^P8````&````!0`8```````!````I($` +M````9FEL93!55`4``R$WD5)U>`L``03U`0``!!0```!02P$"'@,*``````#X +M>7=#!/`L``03U`0``!!0```!02P4&``````(``@"6````B@`````` +` +end diff --git a/libarchive/test/test_read_format_zip_padded2.zip.uu b/libarchive/test/test_read_format_zip_padded2.zip.uu new file mode 100644 index 000000000..3b57fe12e --- /dev/null +++ b/libarchive/test/test_read_format_zip_padded2.zip.uu @@ -0,0 +1,15 @@ +begin 644 test_read_format_zip_padded2.zip +M4$L#!`H``````/=Y=T-%QC+[!@````8````%`!P`9FEL93!55`D``R$WD5(A +M-Y%2=7@+``$$]0$```04````9FEL93`*4$L#!`H``````/AY=T,$]RGB!@`` +M``8````%`!P`9FEL93%55`D``R0WD5(D-Y%2=7@+``$$]0$```04````9FEL +M93$*4$L!`AX#"@``````]WEW0T7&,OL&````!@````4`&````````0```*2! +M`````&9I;&4P550%``,A-Y%2=7@+``$$]0$```04````4$L!`AX#"@`````` +M^'EW0P3W*>(&````!@````4`&````````0```*2!10```&9I;&4Q550%``,D +M-Y%2=7@+``$$]0$```04````4$L%!@`````"``(`E@```(H``````(.2J;)Q +M[PX@J`9,J[C2AH>N<8_.US0'*2[BOW;O(NTEX5WU&F@8Z'+:Q]HTV@/M"D"9 +MPM-+&=&VJIL(Z1VGT?_2.G?,\!@N+_"U8I!K>[,)F8(HA0[3P7N"0[A;4]3QYC6?U,X[!@NM% +MOQJNIW"H3++]CW52#?CS&A=VU8&T:-YPI'J&>3^,1.F=97>R\P]*YF).R*I0 +M2P,$"@``````]WEW0T7&,OL&````!@````4`'`!F:6QE,%54"0`#(3>14B$W +MD5)U>`L``03U`0``!!0```!F:6QE,`I02P,$"@``````^'EW0P3W*>(&```` +M!@````4`'`!F:6QE,554"0`#)#>14B0WD5)U>`L``03U`0``!!0```!F:6QE +M,0I02P$"'@,*``````#W>7=#1<8R^P8````&````!0`8```````!````I($` +M````9FEL93!55`4``R$WD5)U>`L``03U`0``!!0```!02P$"'@,*``````#X +M>7=#!/`L``03U`0``!!0```!02P4&``````(``@"6````B@``````D$,:4/1\ +MSHGT7I+41*.O21/O.+Z%^`_>)S-WGU>>J4G0Y?5'GJ1PMY->*=#3Y+U(_A)OCL1N.):>IH8%T>Y$?&=@(K; +M(+HD_4?R.6`$;@@+ +M12?TRT^S)&!1W*^J06(&KL+*+>]_YL$5K0Q:G9J,1!%[\Q"5;/MD-K-YJW