These parsers run on fully game-controlled buffers (reachable via the
various sceAtracSetData*/sceAtracSetHalfwayBuffer* HLE calls), so a
malicious/malformed game can supply arbitrary bytes here:
- AnalyzeAtracTrack's RIFF chunk-walking loop computed `offset +=
chunk + (chunk & 1)` (all in 32-bit) and only bounds-checked
afterwards. A crafted chunk size could wrap `offset` (and the
`offset + 12` check itself) around, bypassing the bounds check
entirely - Read32(), whose offset parameter is a plain `int`, would
then read from a wild pointer far outside the buffer. Do the
validation in 64-bit before mutating offset, mirroring the pattern
already used by the newer ParseWaveAT3 parser.
- AnalyzeAA3Track validated `size >= tagSize + 36` but then read up to
relative index 35 after rebasing by 10+tagSize - i.e. absolute index
tagSize+45, 10 bytes past what was actually checked.
- The SMPL chunk's loop count (checkNumLoops) was only checked for
being negative, not bounded against the chunk's actual size, so a
crafted value near INT_MAX would drive an unbounded (up to ~2
billion entry) vector::resize() - an easy crash/OOM. The same
unclamped value also let the fill loop below run past the end of
the chunk, since its bound compares the loop counter to chunkSize
rather than the byte offset actually being advanced (24 bytes/loop).
Clamping checkNumLoops to what the chunk can actually hold fixes
both.
- ParseAA3Headers checked for at least 9 bytes but the "ea3"/"id3"
branch it guards reads up through byte index 9, needing 10.
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.
Need to make sure a buffer size check happens before the buffer is
accessed - since the game calls sceAtracSetDataAndGetID with both buffer
and bufferSize as 0, and it expects to receive TOO_SMALL and not a
memory exception.
Thanks to @sum2012 for quick investigationh help.