Fix ZIP parser issue recording empty file entries

If csize (and usize) are 0, like with a directory or other empty file
entry, then the functionionality to record file record information when
indexing the central directory and each associated file record will
neglect to store the `local_header_offset` or `local_header_size`.
That causes problems later after sorting the file records and then
checking for overlapping files.

CLAM-2884
This commit is contained in:
Val S. 2025-10-12 19:12:06 -04:00
parent 87e389d1b2
commit e98b0075e6
No known key found for this signature in database
GPG key ID: 3A7D293D8274CA1B

View file

@ -717,17 +717,35 @@ static cl_error_t parse_local_file_header(
zip += LOCAL_HEADER_elen; zip += LOCAL_HEADER_elen;
bytes_remaining -= LOCAL_HEADER_elen; bytes_remaining -= LOCAL_HEADER_elen;
if (!csize) { /* FIXME: what's used for method0 files? csize or usize? Nothing in the specs, needs testing */
cli_dbgmsg("cli_unzip: local header - skipping empty file\n");
} else {
if (bytes_remaining < csize) { if (bytes_remaining < csize) {
cli_dbgmsg("cli_unzip: local header - stream out of file\n"); cli_dbgmsg("cli_unzip: local header - stream out of file\n");
status = CL_EPARSE; status = CL_EPARSE;
goto done; goto done;
} }
if (NULL != record) {
/* Don't actually unzip if we're just collecting the file record information (offset, sizes) */ /* Don't actually unzip if we're just collecting the file record information (offset, sizes) */
if (NULL == record) { if (NULL == original_filename) {
record->original_filename = NULL;
} else {
record->original_filename = CLI_STRNDUP(original_filename, strlen(original_filename));
}
record->local_header_offset = loff;
record->local_header_size = zip - local_header;
record->compressed_size = csize;
record->uncompressed_size = usize;
record->method = LOCAL_HEADER_method;
record->flags = LOCAL_HEADER_flags;
record->encrypted = (LOCAL_HEADER_flags & F_ENCR) ? 1 : 0;
status = CL_SUCCESS;
} else {
/*
* Unzip or decompress & then unzip.
*/
if (!csize) { /* FIXME: what's used for method0 files? csize or usize? Nothing in the specs, needs testing */
cli_dbgmsg("cli_unzip: local header - skipping empty file\n");
} else {
zip = fmap_need_ptr_once(ctx->fmap, zip, csize); zip = fmap_need_ptr_once(ctx->fmap, zip, csize);
if (NULL == zip) { if (NULL == zip) {
cli_dbgmsg("cli_unzip: local header - data out of file\n"); cli_dbgmsg("cli_unzip: local header - data out of file\n");
@ -751,26 +769,11 @@ static cl_error_t parse_local_file_header(
goto done; goto done;
} }
} }
} else {
if (NULL == original_filename) {
record->original_filename = NULL;
} else {
record->original_filename = CLI_STRNDUP(original_filename, strlen(original_filename));
} }
record->local_header_offset = loff;
record->local_header_size = zip - local_header;
record->compressed_size = csize;
record->uncompressed_size = usize;
record->method = LOCAL_HEADER_method;
record->flags = LOCAL_HEADER_flags;
record->encrypted = (LOCAL_HEADER_flags & F_ENCR) ? 1 : 0;
status = CL_SUCCESS;
} }
zip += csize; zip += csize;
bytes_remaining -= csize; bytes_remaining -= csize;
}
if (LOCAL_HEADER_flags & F_USEDD) { if (LOCAL_HEADER_flags & F_USEDD) {
if (bytes_remaining < 12) { if (bytes_remaining < 12) {
@ -910,8 +913,8 @@ static cl_error_t parse_central_directory_file_header(
central_header = fmap_need_off(ctx->fmap, central_file_header_offset, SIZEOF_CENTRAL_HEADER); central_header = fmap_need_off(ctx->fmap, central_file_header_offset, SIZEOF_CENTRAL_HEADER);
if (NULL == central_header) { if (NULL == central_header) {
cli_dbgmsg("cli_unzip: central header - file header offset out of file\n"); cli_dbgmsg("cli_unzip: central header - reached end of central directory.\n");
status = CL_EPARSE; status = CL_BREAK;
goto done; goto done;
} }