SimpleAudioDec: fix unsigned underflow and unvalidated stream-data size

FindNextMp3Sync() computed `sourcebuff.size() - 2` as the loop bound;
when size() is 0 or 1 this underflows to a huge size_t, turning the
scan into an out-of-bounds read. Reachable via sceMp3NotifyAddStreamData
followed by sceMp3Decode with as little as 1 pending byte.

AuNotifyAddStreamData() trusted the game-supplied `size` outright: a
negative value would make sourcebuff.resize() attempt a huge
allocation (via size_t underflow), an unbounded positive value grows
sourcebuff without limit, and the validated range didn't match the
actual read range (checked [AuBuf, AuBuf+size) while reading from
[AuBuf+offset, AuBuf+offset+size)). Validate size is positive and
capped to the buffer's declared capacity, and validate the range
actually read.
This commit is contained in:
Henrik Rydgård committed 2026-08-12 09:43:23 +02:00
1 parent 029066a977
commit bfbe44ad1c
1 file changed
+11 -1
+11 -1
View File
@@ -481,6 +481,11 @@ AuCtx::~AuCtx() {
}
size_t AuCtx::FindNextMp3Sync() {
// sourcebuff.size() - 2 underflows to a huge size_t when size() is 0 or 1,
// turning this into an out-of-bounds scan - guard against that explicitly.
if (sourcebuff.size() < 3) {
return 0;
}
for (size_t i = 0; i < sourcebuff.size() - 2; ++i) {
if ((sourcebuff[i] & 0xFF) == 0xFF && (sourcebuff[i + 1] & 0xC0) == 0xC0) {
return i;
@@ -603,7 +608,12 @@ u32 AuCtx::AuNotifyAddStreamData(int size) {
AuBufAvailable += size;
}
if (Memory::IsValidRange(AuBuf, size)) {
// `size` is game-supplied and was previously trusted outright: a negative value
// would make sourcebuff.resize() attempt a huge allocation (size_t underflow),
// and an unbounded positive value would grow sourcebuff without limit (DoS).
// The validated range also has to match what's actually read below - it was
// checking [AuBuf, AuBuf+size) while the copy reads from [AuBuf+offset, ...).
if (size > 0 && size <= (int)AuBufSize && Memory::IsValidRange(AuBuf + offset, size)) {
sourcebuff.resize(sourcebuff.size() + size);
Memory::MemcpyUnchecked(&sourcebuff[sourcebuff.size() - size], AuBuf + offset, size);
}