Skip to content

Commit 15c6484

Browse files
committed
file/archive_file_zlib: guard ZIP context allocation
zip_parse_file_init() reads the central-directory size and offset from the ZIP footer and allocates "sizeof(zip_context_t) + directory_size" bytes for the context + directory. Three issues: 1. 32-bit allocation wraparound. On 32-bit hosts (3DS, Vita, PSP, Wii, Wii U) size_t is 32 bits, so a crafted directory_size near UINT32_MAX makes the "+ sizeof(zip_context_t)" wrap to a tiny value. The subsequent directory_end = directory + directory_size computation then points roughly 4 GiB past a small allocation, and every read from the directory is out of bounds. 2. Unchecked malloc return. Even on 64-bit, a 4 GiB directory_size request from a crafted ZIP can fail allocation; the next line dereferences the returned NULL unconditionally. 3. Incomplete sanity check. The existing check only verifies that directory_size and directory_offset individually fit within the archive. With offset = archive_size - 1 and size = archive_size, each passes but the combination claims the directory runs past EOF. The subsequent short read is caught, but only after the large bogus allocation is already made. Fix: reject the combined "offset + size > archive_size", reject sizes that would overflow the allocation, and check the malloc return.
1 parent 1235db3 commit 15c6484

1 file changed

Lines changed: 17 additions & 0 deletions

File tree

libretro-common/file/archive_file_zlib.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,10 +508,27 @@ static int zip_parse_file_init(file_archive_transfer_t *state,
508508
|| directory_offset > state->archive_size)
509509
return -1;
510510

511+
/* Combined sanity check: the directory must fit entirely within
512+
* the archive. Without this, offset + size could wrap or point
513+
* past EOF, producing a large bogus allocation and a short read. */
514+
if ((int64_t)directory_offset + (int64_t)directory_size > state->archive_size)
515+
return -1;
516+
517+
/* Reject sizes that would overflow the allocation on 32-bit hosts.
518+
* size_t is 32-bit on 3DS / Vita / PSP / Wii / Wii U, where a
519+
* directory_size near UINT32_MAX would wrap
520+
* sizeof(zip_context_t) + (size_t)directory_size
521+
* to a tiny value, after which directory_end runs off the end of
522+
* the allocation. */
523+
if ((size_t)directory_size > SIZE_MAX - sizeof(zip_context_t))
524+
return -1;
525+
511526
/* This is a ZIP file, allocate one block of memory for both the
512527
* context and the entire directory, then read the directory.
513528
*/
514529
zip_context = (zip_context_t*)malloc(sizeof(zip_context_t) + (size_t)directory_size);
530+
if (!zip_context)
531+
return -1;
515532
zip_context->state = state;
516533
zip_context->directory = (uint8_t*)(zip_context + 1);
517534
zip_context->directory_entry = zip_context->directory;

0 commit comments

Comments
 (0)