A review of their savestates found nothing wrong with a normal save and
load, but a broken or crafted state could crash or write out of bounds:
- Atrac2's context pointer was dereferenced and its SAS buffer index used
unchecked. Atrac v1's file size sized an allocation before it was known
to be in the file, and its first buffer could be larger than the file.
- MediaEngine's MPEG header size and read position index a fixed array,
and its ringbuffer size sizes buffers.
- AuCtx's PCM buffer must hold two frames, as the game side requires. And
AuDecode compared a decoded frame with the whole PCM buffer instead of
the half it fills, so a frame between the two zero-filled a negative
size.
- BufferQueue's fill level, VAG block positions (now ending the voice,
like the other bad VAG state), sceAudiocodec's context count, the SRC
mix frequency (divided by), and old sceMpeg states' ringbuffer address.
- SAS decoding with an atrac id that has no atrac crashed after warning.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A crafted RIFF smpl chunk could put the stream read offset past the end
of the file. CalculateStreamInfo() and AddStreamData() then subtracted it
from the file size in unsigned arithmetic, the remaining size wrapped, and
the guest's data was copied to a guest-chosen distance past dataBuf_.
Check the offset before subtracting, and route every copy into dataBuf_
through a helper that clamps it to the file. SeekToSample() no longer
prefills from past the end, and dataBuf_'s slack covers a whole frame.
This is the old Atrac implementation, which is only used when chosen in
the config or when loading old savestates.
Discovered and reported by redthing1 (https://github.com/redthing1) of
V12 Security.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
libatrac3plus picks the codec parameter from the frame size and the
header's joint stereo flag, and the channel count plays no part. We
guessed joint stereo from the frame size and channel count instead, so
LocoRoco 2's MuiMui house music never started: the game writes a 2-channel
normal-stereo header for every track it streams, and that 0xC0 track holds
one mono sound unit per frame. Taken as joint stereo, its first frame failed
during setup, and the game retried forever.
Now the joint stereo flag comes from the track header, and the decoder's
channel count from the parameter it maps to, so that track decodes as mono
into both output channels, as on hardware. Low-level decoding, which has
no header, still goes by the frame size. Atrac2 saves the flag; older
states fall back to the guess.
Fixes#8647.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The space-left clamp was computed in unsigned 32-bit arithmetic, so a
crafted fileoffset/FirstOffsetExtra could underflow to a huge value and
leave addbytes unclamped, driving a Memory::Memcpy past dataBuf_.
Compute the clamp in signed 64-bit, clamp negatives to 0, and skip the
copy when there's nothing to write.