ExtractZipContents wrote every entry's declared size with no ceiling, so
a small high-ratio zip could decompress to fill the storage device.
- Add a maxTotalSize parameter to ExtractZipContents (default 4GB) and
ExtractFile.
- Bail out in the size-summation pass when the total declared size
exceeds the limit, and again per write chunk in case declared sizes
are inaccurate.
AnalyzeAtracTrack used max(fileSize, size) as the chunk-parse bound with
fileSize taken from the file's RIFF header, so a crafted inflated RIFF
size could push reads past the end of the buffer. Keep the real-library
behavior of tolerating a too-low size, but clamp the parse bound to the
actual mapped guest memory at the buffer.
Also guard ParseWaveAT3's RIFF scan against a blockSize < 4 underflow
that could make the offset negative and bypass the loop bounds check, and
clamp readSize to the mapped region in Atrac2::SetData before parsing.
pmf_init reported stream dimensions without a cap, and PMFView::Draw
allocated width * height * 4 with 32-bit int arithmetic, so a crafted
ICON1.PMF could overflow the allocation to a small buffer while
sws_scale wrote the full frame.
- Reject videos with dimensions outside 1..720x480 in pmf_init (PMFs on
the PSP never exceed 720x480).
- Use size_t arithmetic for the frame buffer allocation.
A crafted zip with a parent-directory ("..") entry name could escape the
destination directory during extraction, writing arbitrary files on the
host (e.g. into startup/autostart folders). ExtractZipContents built the
output path by concatenating the raw zip entry name onto the destination
with no traversal check.
Changes:
- Add HasParentDirComponent() utility in Core/Util/PathUtil and use it in
GameManager::ExtractZipContents to reject entries with a ".." component.
Guard both the directory-creation and file-writing passes.
- Expose ExtractZipContents as public for testing.
- Add unittest/TestZipSlip which crafts a zip with a "../evil.txt" entry
and verifies it is not written outside the destination directory.
Serializing the kernel memory block lists (userMemory, kernelMemory,
volatileMemory) wrote a full Section header per block and re-zeroed
each block's tag padding with strlen+memset on every save. Games keep
on the order of a thousand blocks alive, so the per-block overhead is
both measurable save time and wasted payload bytes.
Zero-pad tags once at write time instead (Block constructor and
SetAllocated), so the v2 form can store blocks raw: start, size, taken,
tag - no per-block section machinery, no per-save tag scrubbing.
Uninitialized padding still never reaches the stream, since every path
that writes a tag now clears it first.
v1 states still load through the old per-block-Section form, which is
kept unchanged.