Fix out-of-bounds reads in ATRAC track parsing

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.
This commit is contained in:
Henrik Rydgård
2026-08-01 11:57:27 +02:00
parent 32abdd63bf
commit 4cd611b71f
2 changed files with 16 additions and 1 deletions
+3 -1
View File
@@ -975,7 +975,9 @@ int Atrac2::SetData(const Track &track, u32 bufferAddr, u32 readSize, u32 buffer
// Turns out that games can abuse bufferSize, so we can't verify that it's a valid length with GetPointerRange.
const u8 *bufferPtr = Memory::GetPointerUnchecked(bufferAddr);
if (!Memory::IsValidRange(bufferAddr, readSize)) {
WARN_LOG(Log::Atrac, "Atrac2::SetData: Bad buffer range %08x+%08x - however, proceeeding.", bufferAddr, readSize);
WARN_LOG(Log::Atrac, "Atrac2::SetData: Bad buffer range %08x+%08x - clamping to mapped size.", bufferAddr, readSize);
// Clamp so the parsers below can't read past the mapped region.
readSize = Memory::ClampValidSizeAt(bufferAddr, readSize);
}
if (!isAA3) {
int retval = ParseWaveAT3(bufferPtr, readSize, &trackInfo);
+13
View File
@@ -84,7 +84,14 @@ int AnalyzeAtracTrack(const u8 *buffer, u32 size, Track *track, std::string *err
track->fileSize = Read32(buffer, offset - 8) + 8;
// Even if the RIFF size is too low, it may simply be incorrect. This works on real firmware.
// But the reads below must stay within mapped guest memory: clamp the
// parse bound to the actual mapped region at the buffer, so a crafted,
// inflated RIFF size can't push the reads past the end of RAM.
u32 maxSize = std::max(track->fileSize, size);
const u32 bufferAddr = Memory::GetAddressFromHostPointer(buffer);
if (bufferAddr != 0) {
maxSize = std::min(maxSize, Memory::MaxSizeAtAddress(bufferAddr));
}
bool bfoundData = false;
u32 dataChunkSize = 0;
@@ -375,6 +382,12 @@ int ParseWaveAT3(const u8 *data, u32 dataLength, TrackInfo *track) {
// We found the WAVE header.
break;
}
// Guard against underflow (blockSize < 4) making the offset negative
// and bypassing the loop bounds check, and against advancing past the
// end of the buffer.
if (blockSize < 4 || (u64)offset + blockSize - 4 > dataLength) {
return SCE_ERROR_ATRAC_SIZE_TOO_SMALL;
}
offset += blockSize - 4;
}