fix(audio): bounds-check fontId to stop OOB crash with large SAF packs (#6916)

AudioLoad_IsFontLoadComplete had a stub `return true` that bypassed all
load-status checks, masking an out-of-bounds write: for large SAF packs
(many custom sequences) SetFontLoadStatus indexed fontLoadStatus[] with
fontId values larger than the fontMapSize-sized array, causing heap
corruption and semi-random crashes.

Remove the stub. Add a (size_t)fontId >= fontMapSize guard in both the
check and the setter: out-of-range IDs (custom SAF sequences that carry
no associated soundfont) are treated as "loaded" in the check and
silently skipped in the write, matching prior observable behavior while
eliminating the OOB access.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
David Racine
2026-07-13 08:12:43 -04:00
committed by GitHub
parent ff7eb482d4
commit fdb7e194cc
+10 -9
View File
@@ -297,18 +297,19 @@ void AudioLoad_InitSampleDmaBuffers(s32 arg0) {
gAudioContext.sampleDmaReuseQueue2WrPos = gAudioContext.sampleDmaCount - gAudioContext.sampleDmaListSize1;
}
// SOH [Port] Completely reworked from decomp: SAF custom fontIds can exceed the native table; bounds-check against
// fontMapSize to avoid OOB reads.
s32 AudioLoad_IsFontLoadComplete(s32 fontId) {
return true;
if (fontId == 0xFF) {
return true;
} else if (gAudioContext.fontLoadStatus[fontId] >= 2) {
return true;
} else if (gAudioContext.fontLoadStatus[AudioLoad_GetRealTableIndex(FONT_TABLE, fontId)] >= 2) {
return true;
} else {
return false;
}
// Resolve indirection (identity for FONT_TABLE today, but kept for parity with other tables).
fontId = (s32)AudioLoad_GetRealTableIndex(FONT_TABLE, (u32)fontId);
if ((size_t)fontId >= fontMapSize) {
// No entry in the font map — SAF sequence with no associated soundfont, treat as ready.
return true;
}
return gAudioContext.fontLoadStatus[fontId] >= 2;
}
s32 AudioLoad_IsSeqLoadComplete(s32 seqId) {
@@ -336,7 +337,7 @@ s32 AudioLoad_IsSampleLoadComplete(s32 sampleBankId) {
}
void AudioLoad_SetFontLoadStatus(s32 fontId, s32 status) {
if ((fontId != 0xFF) && (gAudioContext.fontLoadStatus[fontId] != 5)) {
if ((fontId != 0xFF) && ((size_t)fontId < fontMapSize) && (gAudioContext.fontLoadStatus[fontId] != 5)) {
gAudioContext.fontLoadStatus[fontId] = status;
}
}