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 <vda.linux@googlemail.com>
This commit is contained in:
parent
84ba50c32f
commit
0ccf52a9fb
@ -45,6 +45,12 @@
|
|||||||
#include "libbb.h"
|
#include "libbb.h"
|
||||||
#include "bb_archive.h"
|
#include "bb_archive.h"
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
# define dbg(...) bb_error_msg(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define dbg(...) ((void)0)
|
||||||
|
#endif
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
#if BB_BIG_ENDIAN
|
#if BB_BIG_ENDIAN
|
||||||
ZIP_FILEHEADER_MAGIC = 0x504b0304,
|
ZIP_FILEHEADER_MAGIC = 0x504b0304,
|
||||||
@ -193,15 +199,17 @@ static uint32_t find_cdf_offset(void)
|
|||||||
unsigned char *p;
|
unsigned char *p;
|
||||||
off_t end;
|
off_t end;
|
||||||
unsigned char *buf = xzalloc(PEEK_FROM_END);
|
unsigned char *buf = xzalloc(PEEK_FROM_END);
|
||||||
|
uint32_t found;
|
||||||
|
|
||||||
end = xlseek(zip_fd, 0, SEEK_END);
|
end = xlseek(zip_fd, 0, SEEK_END);
|
||||||
end -= PEEK_FROM_END;
|
end -= PEEK_FROM_END;
|
||||||
if (end < 0)
|
if (end < 0)
|
||||||
end = 0;
|
end = 0;
|
||||||
|
dbg("Looking for cdf_offset starting from 0x%"OFF_FMT"x", end);
|
||||||
xlseek(zip_fd, end, SEEK_SET);
|
xlseek(zip_fd, end, SEEK_SET);
|
||||||
full_read(zip_fd, buf, PEEK_FROM_END);
|
full_read(zip_fd, buf, PEEK_FROM_END);
|
||||||
|
|
||||||
cde_header.formatted.cdf_offset = BAD_CDF_OFFSET;
|
found = BAD_CDF_OFFSET;
|
||||||
p = buf;
|
p = buf;
|
||||||
while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
|
while (p <= buf + PEEK_FROM_END - CDE_HEADER_LEN - 4) {
|
||||||
if (*p != 'P') {
|
if (*p != 'P') {
|
||||||
@ -220,14 +228,25 @@ static uint32_t find_cdf_offset(void)
|
|||||||
/*
|
/*
|
||||||
* I've seen .ZIP files with seemingly valid CDEs
|
* I've seen .ZIP files with seemingly valid CDEs
|
||||||
* where cdf_offset points past EOF - ??
|
* where cdf_offset points past EOF - ??
|
||||||
* Ignore such CDEs:
|
* This check ignores such CDEs:
|
||||||
*/
|
*/
|
||||||
if (cde_header.formatted.cdf_offset < end + (p - buf))
|
if (cde_header.formatted.cdf_offset < end + (p - buf)) {
|
||||||
break;
|
found = cde_header.formatted.cdf_offset;
|
||||||
cde_header.formatted.cdf_offset = BAD_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);
|
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)
|
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();
|
cdf_offset = find_cdf_offset();
|
||||||
|
|
||||||
if (cdf_offset != BAD_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);
|
xlseek(zip_fd, cdf_offset + 4, SEEK_SET);
|
||||||
xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
|
xread(zip_fd, cdf_ptr->raw, CDF_HEADER_LEN);
|
||||||
FIX_ENDIANNESS_CDF(*cdf_ptr);
|
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_offset += 4 + CDF_HEADER_LEN
|
||||||
+ cdf_ptr->formatted.file_name_length
|
+ cdf_ptr->formatted.file_name_length
|
||||||
+ cdf_ptr->formatted.extra_field_length
|
+ cdf_ptr->formatted.extra_field_length
|
||||||
@ -532,11 +555,14 @@ int unzip_main(int argc, char **argv)
|
|||||||
/* Check magic number */
|
/* Check magic number */
|
||||||
xread(zip_fd, &magic, 4);
|
xread(zip_fd, &magic, 4);
|
||||||
/* Central directory? It's at the end, so exit */
|
/* 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;
|
break;
|
||||||
|
}
|
||||||
#if ENABLE_DESKTOP
|
#if ENABLE_DESKTOP
|
||||||
/* Data descriptor? It was a streaming file, go on */
|
/* Data descriptor? It was a streaming file, go on */
|
||||||
if (magic == ZIP_DD_MAGIC) {
|
if (magic == ZIP_DD_MAGIC) {
|
||||||
|
dbg("got ZIP_DD_MAGIC");
|
||||||
/* skip over duplicate crc32, cmpsize and ucmpsize */
|
/* skip over duplicate crc32, cmpsize and ucmpsize */
|
||||||
unzip_skip(3 * 4);
|
unzip_skip(3 * 4);
|
||||||
continue;
|
continue;
|
||||||
@ -544,6 +570,7 @@ int unzip_main(int argc, char **argv)
|
|||||||
#endif
|
#endif
|
||||||
if (magic != ZIP_FILEHEADER_MAGIC)
|
if (magic != ZIP_FILEHEADER_MAGIC)
|
||||||
bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
|
bb_error_msg_and_die("invalid zip magic %08X", (int)magic);
|
||||||
|
dbg("got ZIP_FILEHEADER_MAGIC");
|
||||||
|
|
||||||
/* Read the file header */
|
/* Read the file header */
|
||||||
xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN);
|
xread(zip_fd, zip_header.raw, ZIP_HEADER_LEN);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user