From 0ccf52a9fb9fa2f76eacbb6f1ef8419220557a40 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 17 Apr 2016 21:05:34 +0200 Subject: unzip: fix a case where we find wrong CDE. Closes 8821 function old new delta unzip_main 2472 2490 +18 Signed-off-by: Denys Vlasenko --- archival/unzip.c | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/archival/unzip.c b/archival/unzip.c index f41ab6f44..b0a6ca836 100644 --- a/archival/unzip.c +++ b/archival/unzip.c @@ -45,6 +45,12 @@ #include "libbb.h" #include "bb_archive.h" +#if 0 +# define dbg(...) bb_error_msg(__VA_ARGS__) +#else +# define dbg(...) ((void)0) +#endif + enum { #if BB_BIG_ENDIAN ZIP_FILEHEADER_MAGIC = 0x504b0304, @@ -193,15 +199,17 @@ static uint32_t find_cdf_offset(void) unsigned char *p; off_t end; unsigned char *buf = xzalloc(PEEK_FROM_END); + uint32_t found; end = xlseek(zip_fd, 0, SEEK_END); end -= PEEK_FROM_END; if (end < 0) end = 0; - xlseek(zip_fd, end, SEEK_SET); + dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end); + xlseek(zip_fd, end, SEEK_SET); full_read(zip_fd, buf, PEEK_FROM_END); - cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; + found = BAD_CDF_OFFSET; p = buf; while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) { if (*p != 'P') { @@ -220,14 +228,25 @@ static uint32_t find_cdf_offset(void) /* * I've seen .ZIP files with seemingly valid CDEs * where cdf_offset points past EOF - ?? - * Ignore such CDEs: + * This check ignores such CDEs: */ - if (cde_header.formatted.cdf_offset < end + (p - buf)) - break; - cde_header.formatted.cdf_offset = BAD_CDF_OFFSET; + if (cde_header.formatted.cdf_offset < end + (p - buf)) { + found = cde_header.formatted.cdf_offset; + dbg("Possible cdf_offset:0x%x at 0x%"OFF_FMT"x", + (unsigned)found, end + (p-3 - buf)); + dbg(" cdf_offset+cdf_size:0x%x", + (unsigned)(found + SWAP_LE32(cde_header.formatted.cdf_size))); + /* + * We do not "break" here because only the last CDE is valid. + * I've seen a .zip archive which contained a .zip file, + * uncompressed, and taking the first CDE was using + * the CDE inside that file! + */ + } } free(buf); - return cde_header.formatted.cdf_offset; + dbg("Found cdf_offset:0x%x", (unsigned)found); + return found; }; static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) @@ -240,9 +259,13 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr) cdf_offset = find_cdf_offset(); if (cdf_offset != BAD_CDF_OFFSET) { + dbg("Reading CDF at 0x%x", (unsigned)cdf_offset); xlseek(zip_fd, cdf_offset + 4, SEEK_SET); xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN); FIX_ENDIANNESS_CDF(*cdf_ptr); + dbg("file_name_length:%u", (unsigned)cdf_ptr->formatted.file_name_length); + dbg("extra_field_length:%u", (unsigned)cdf_ptr->formatted.extra_field_length); + dbg("file_comment_length:%u", (unsigned)cdf_ptr->formatted.file_comment_length); cdf_offset += 4 + CDF_HEADER_LEN + cdf_ptr->formatted.file_name_length + cdf_ptr->formatted.extra_field_length @@ -532,11 +555,14 @@ int unzip_main(int argc, char **argv) /* Check magic number */ xread(zip_fd, &magic, 4); /* Central directory? It's at the end, so exit */ - if (magic == ZIP_CDF_MAGIC) + if (magic == ZIP_CDF_MAGIC) { + dbg("got ZIP_CDF_MAGIC"); break; + } #if ENABLE_DESKTOP /* Data descriptor? It was a streaming file, go on */ if (magic == ZIP_DD_MAGIC) { + dbg("got ZIP_DD_MAGIC"); /* skip over duplicate crc32, cmpsize and ucmpsize */ unzip_skip(3 * 4); continue; @@ -544,6 +570,7 @@ int unzip_main(int argc, char **argv) #endif if (magic != ZIP_FILEHEADER_MAGIC) bb_error_msg_and_die("invalid zip magic %08X", (int)magic); + dbg("got ZIP_FILEHEADER_MAGIC"); /* Read the file header */ xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN); -- cgit v1.2.3