From eafafe507ebe0dd9bb8917552c840f9f78574189 Mon Sep 17 00:00:00 2001 From: David Racine Date: Mon, 3 Aug 2026 17:05:07 -0400 Subject: fix: seqLoadStatus OOB reads/writes for custom SAF sequences (#6917) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- soh/src/code/audio_load.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'soh/src/code') diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c index 85c0ed709..582cf3a1c 100644 --- a/soh/src/code/audio_load.c +++ b/soh/src/code/audio_load.c @@ -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) { + } + if ((size_t)seqId >= sequenceMapSize) { + // Custom SAF sequence whose seqId exceeds the status table — not async-loading, treat as ready. return true; - } else if (gAudioContext.seqLoadStatus[AudioLoad_GetRealTableIndex(SEQUENCE_TABLE, seqId)] >= 2) { + } + 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; - } else { - return false; } + 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; } -- cgit v1.2.3