fix: seqLoadStatus OOB reads/writes for custom SAF sequences (#6917)

Custom SAF sequence IDs can exceed sequenceMapSize, causing out-of-bounds
reads/writes on the seqLoadStatus byte array (sized exactly sequenceMapSize).

This is the direct cause of intermittent battle music failure with BGM packs
(issue #5706): on the second encounter, AudioLoad_SyncLoadSeq reads a garbage
value from heap memory past seqLoadStatus[] and may find 1 (loading in
progress), causing it to return NULL early — the sequence player is never
initialized and no battle music plays.

Symmetric fix to the fontLoadStatus guards in PR #6916:
- AudioLoad_IsSeqLoadComplete: return true for OOB seqIds (custom SAF
  sequences are resource-manager-backed, not in the async-load status table)
- AudioLoad_SetSeqLoadStatus: skip update for seqId >= sequenceMapSize
- AudioLoad_SyncLoadSeq: skip the in-progress check for OOB seqIds

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
David Racine
2026-08-03 17:05:07 -04:00
committed by GitHub
parent 6889132708
commit eafafe507e
+14 -8
View File
@@ -315,13 +315,19 @@ s32 AudioLoad_IsFontLoadComplete(s32 fontId) {
s32 AudioLoad_IsSeqLoadComplete(s32 seqId) {
if (seqId == 0xFF) {
return true;
} else if (gAudioContext.seqLoadStatus[seqId] >= 2) {
return true;
} else if (gAudioContext.seqLoadStatus[AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId)] >= 2) {
return true;
} else {
return false;
}
if ((size_t)seqId >= sequenceMapSize) {
// Custom SAF sequence whose seqId exceeds the status table — not async-loading, treat as ready.
return true;
}
if (gAudioContext.seqLoadStatus[seqId] >= 2) {
return true;
}
s32 realId = (s32)AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId);
if ((size_t)realId < sequenceMapSize && gAudioContext.seqLoadStatus[realId] >= 2) {
return true;
}
return false;
}
s32 AudioLoad_IsSampleLoadComplete(s32 sampleBankId) {
@@ -343,7 +349,7 @@ void AudioLoad_SetFontLoadStatus(s32 fontId, s32 status) {
}
void AudioLoad_SetSeqLoadStatus(s32 seqId, s32 status) {
if ((seqId != 0xFF) && (gAudioContext.seqLoadStatus[seqId] != 5)) {
if ((seqId != 0xFF) && ((size_t)seqId < sequenceMapSize) && (gAudioContext.seqLoadStatus[seqId] != 5)) {
gAudioContext.seqLoadStatus[seqId] = status;
}
}
@@ -637,7 +643,7 @@ u8* AudioLoad_SyncLoadSeq(s32 seqId) {
s32 pad;
s32 didAllocate;
if (gAudioContext.seqLoadStatus[AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId)] == 1) {
if ((size_t)seqId < sequenceMapSize && gAudioContext.seqLoadStatus[seqId] == 1) {
return NULL;
}