Skip to content

Commit

Permalink
libzipfile: add additional validity checks.
Browse files Browse the repository at this point in the history
- Make sure the start and end of the data for a given entry are
  within the allocated buffer.
- Make sure all central directory entries start and end within the
  central directory buffer.
- Reject zip file entries that have no filenames.

CYNGNOS-3303
bug: 30916186
test: test_zipfile with known bad zip files.
Change-Id: Ibf3f6469e60c85ec1608f5ce613d40867d2d09b7
  • Loading branch information
narayank authored and Jessica Wagantall committed Nov 16, 2016
1 parent 8dc5fd6 commit 5479772
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
56 changes: 54 additions & 2 deletions libzipfile/centraldir.c
Expand Up @@ -62,18 +62,25 @@ read_central_dir_values(Zipfile* file, const unsigned char* buf, int len)
return 0;
}

static const int kCompressionStored = 0x0;
static const int kCompressionDeflate = 0x8;

static int
read_central_directory_entry(Zipfile* file, Zipentry* entry,
const unsigned char** buf, ssize_t* len)
{
const unsigned char* p;
size_t remaining;
const unsigned char* bufLimit;

unsigned short extraFieldLength;
unsigned short fileCommentLength;
unsigned long localHeaderRelOffset;
unsigned int dataOffset;

p = *buf;
remaining = *len;
bufLimit = file->buf + file->bufsize;

if (*len < ENTRY_LEN) {
fprintf(stderr, "cde entry not large enough\n");
Expand All @@ -94,31 +101,76 @@ read_central_directory_entry(Zipfile* file, Zipentry* entry,
localHeaderRelOffset = read_le_int(&p[0x2a]);

p += ENTRY_LEN;
remaining -= ENTRY_LEN;

// filename
if (entry->fileNameLength != 0) {
if (entry->fileNameLength > remaining) {
fprintf(stderr, "cde entry not large enough for file name.\n");
return 1;
}

entry->fileName = p;
} else {
entry->fileName = NULL;
fprintf(stderr, "cde entry does not contain a file name.\n");
return 1;
}
p += entry->fileNameLength;
remaining -= entry->fileNameLength;

// extra field
// extra field, if any
if (extraFieldLength > remaining) {
fprintf(stderr, "cde entry not large enough for extra field.\n");
return 1;
}
p += extraFieldLength;
remaining -= extraFieldLength;

// comment, if any
if (fileCommentLength > remaining) {
fprintf(stderr, "cde entry not large enough for file comment.\n");
return 1;
}
p += fileCommentLength;
remaining -= fileCommentLength;

*buf = p;
*len = remaining;

// the size of the extraField in the central dir is how much data there is,
// but the one in the local file header also contains some padding.
p = file->buf + localHeaderRelOffset;
if (p >= bufLimit) {
fprintf(stderr, "Invalid local header offset for entry.\n");
return 1;
}

extraFieldLength = read_le_short(&p[0x1c]);

dataOffset = localHeaderRelOffset + LFH_SIZE
+ entry->fileNameLength + extraFieldLength;
entry->data = file->buf + dataOffset;

// Sanity check: make sure that the start of the entry data is within
// our allocated buffer.
if ((entry->data < file->buf) || (entry->data >= bufLimit)) {
fprintf(stderr, "Invalid data offset for entry.\n");
return 1;
}

// Sanity check: make sure that the end of the entry data is within
// our allocated buffer. We need to look at the uncompressedSize for
// stored entries and the compressed size for deflated entries.
if ((entry->compressionMethod == kCompressionStored) &&
(entry->uncompressedSize > (unsigned int) (bufLimit - entry->data))) {
fprintf(stderr, "Invalid uncompressed size for stored entry.\n");
return 1;
}
if ((entry->compressionMethod == kCompressionDeflate) &&
(entry->compressedSize > (unsigned int) (bufLimit - entry->data))) {
fprintf(stderr, "Invalid uncompressed size for deflated entry.\n");
return 1;
}
#if 0
printf("file->buf=%p entry->data=%p dataOffset=%x localHeaderRelOffset=%d "
"entry->fileNameLength=%d extraFieldLength=%d\n",
Expand Down
1 change: 0 additions & 1 deletion libzipfile/test_zipfile.c
Expand Up @@ -75,7 +75,6 @@ main(int argc, char** argv)
unsize = get_zipentry_size(entry);
size = unsize * 1.001;
scratch = malloc(size);
printf("scratch=%p\n", scratch);
err = decompress_zipentry(entry, scratch, size);
if (err != 0) {
fprintf(stderr, "error decompressing file\n");
Expand Down

0 comments on commit 5479772

Please sign in to comment.