[git commit] unzip: survive lack of CDF on non-streaming zip files

Denys Vlasenko vda.linux at googlemail.com
Sun Jul 21 00:31:08 UTC 2013


commit: http://git.busybox.net/busybox/commit/?id=26cd90c7fd32f2ef252acb146f8b9152e55346ec
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 archival/unzip.c |   55 ++++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 39 insertions(+), 16 deletions(-)

diff --git a/archival/unzip.c b/archival/unzip.c
index c250d75..673e5fe 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -172,6 +172,9 @@ enum { zip_fd = 3 };
  */
 #define PEEK_FROM_END (64*1024)
 
+/* This value means that we failed to find CDF */
+#define BAD_CDF_OFFSET ((uint32_t)0xffffffff)
+
 /* NB: does not preserve file position! */
 static uint32_t find_cdf_offset(void)
 {
@@ -187,6 +190,7 @@ static uint32_t find_cdf_offset(void)
 	xlseek(zip_fd, end, SEEK_SET);
 	full_read(zip_fd, buf, PEEK_FROM_END);
 
+	cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
 	p = buf;
 	while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
 		if (*p != 'P') {
@@ -202,11 +206,17 @@ static uint32_t find_cdf_offset(void)
 		/* we found CDE! */
 		memcpy(cde_header.raw, p + 1, CDE_HEADER_LEN);
 		FIX_ENDIANNESS_CDE(cde_header);
-		free(buf);
-		return cde_header.formatted.cdf_offset;
+		/*
+		 * I've seen .ZIP files with seemingly valid CDEs
+		 * where cdf_offset points past EOF - ??
+		 * Ignore such CDEs:
+		 */
+		if (cde_header.formatted.cdf_offset < end + (p - buf))
+			break;
+		cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
 	}
-	//free(buf);
-	bb_error_msg_and_die("can't find file table");
+	free(buf);
+	return cde_header.formatted.cdf_offset;
 };
 
 static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
@@ -218,13 +228,15 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
 	if (!cdf_offset)
 		cdf_offset = find_cdf_offset();
 
-	xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
-	xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
-	FIX_ENDIANNESS_CDF(*cdf_ptr);
-	cdf_offset += 4 + CDF_HEADER_LEN
-		+ cdf_ptr->formatted.file_name_length
-		+ cdf_ptr->formatted.extra_field_length
-		+ cdf_ptr->formatted.file_comment_length;
+	if (cdf_offset != BAD_CDF_OFFSET) {
+		xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
+		xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
+		FIX_ENDIANNESS_CDF(*cdf_ptr);
+		cdf_offset += 4 + CDF_HEADER_LEN
+			+ cdf_ptr->formatted.file_name_length
+			+ cdf_ptr->formatted.extra_field_length
+			+ cdf_ptr->formatted.file_comment_length;
+	}
 
 	xlseek(zip_fd, org, SEEK_SET);
 	return cdf_offset;
@@ -233,8 +245,9 @@ static uint32_t read_next_cdf(uint32_t cdf_offset, cdf_header_t *cdf_ptr)
 
 static void unzip_skip(off_t skip)
 {
-	if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
-		bb_copyfd_exact_size(zip_fd, -1, skip);
+	if (skip != 0)
+		if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1)
+			bb_copyfd_exact_size(zip_fd, -1, skip);
 }
 
 static void unzip_create_leading_dirs(const char *fn)
@@ -535,21 +548,31 @@ int unzip_main(int argc, char **argv)
 			bb_error_msg_and_die("zip flag 1 (encryption) is not supported");
 		}
 
-		{
+		if (cdf_offset != BAD_CDF_OFFSET) {
 			cdf_header_t cdf_header;
 			cdf_offset = read_next_cdf(cdf_offset, &cdf_header);
+			/*
+			 * Note: cdf_offset can become BAD_CDF_OFFSET after the above call.
+			 */
 			if (zip_header.formatted.zip_flags & SWAP_LE16(0x0008)) {
 				/* 0x0008 - streaming. [u]cmpsize can be reliably gotten
-				 * only from Central Directory. See unzip_doc.txt */
+				 * only from Central Directory. See unzip_doc.txt
+				 */
 				zip_header.formatted.crc32    = cdf_header.formatted.crc32;
 				zip_header.formatted.cmpsize  = cdf_header.formatted.cmpsize;
 				zip_header.formatted.ucmpsize = cdf_header.formatted.ucmpsize;
 			}
 			if ((cdf_header.formatted.version_made_by >> 8) == 3) {
-				/* this archive is created on Unix */
+				/* This archive is created on Unix */
 				dir_mode = file_mode = (cdf_header.formatted.external_file_attributes >> 16);
 			}
 		}
+		if (cdf_offset == BAD_CDF_OFFSET
+		 && (zip_header.formatted.zip_flags & SWAP_LE16(0x0008))
+		) {
+			/* If it's a streaming zip, we _require_ CDF */
+			bb_error_msg_and_die("can't find file table");
+		}
 #endif
 
 		/* Read filename */


More information about the busybox-cvs mailing list