summaryrefslogtreecommitdiff
path: root/soh/src/code/audio_load.c
diff options
context:
space:
mode:
authorPedro Nascimento <pnascimento@gmail.com>2026-08-09 15:36:33 -0300
committerGitHub <noreply@github.com>2026-08-09 18:36:33 +0000
commit4e39d06accfa65fbed9e034c3cd7face91a6d80c (patch)
tree0bf4ee8560558d87a1972ea4dcd6627e68c853b2 /soh/src/code/audio_load.c
parent490e13616ed1205c34ac2ac3a8f1342495206f39 (diff)
fix(audio): bound audio-heap cache tables (SIGSEGV with large custom music packs) (#6932)
Root cause of three identical field crashes (audio thread, opcode fetch through a pointer with its low 32 bits overwritten, seconds after scene transitions): AudioHeap_AllocPermanent writes permanentCache[index] with index = permanentPool.count and no bound against the 32-entry array. In SoH every soundfont sync-load is forced permanent, and custom sequences whose SEQ.xml says CachePolicy="Temporary" ALSO allocate permanently (the factory stores the LUS enum where CACHE_TEMPORARY == 0, while AudioLoad_SyncLoad's switch reads 0 with the ROM convention 'permanent'). A pack with ~60 streamed customs plus vanilla fonts pushes count past 32 within a session, after which each allocation sprays a {ptr, size, tableType/id} triplet at 24-byte stride through gAudioContext - entry[135]'s ptr field lands exactly on seqPlayers[0].scriptState.pc and entry[156] on seqPlayers[1]'s (both verified against the crash-dump registers). - permanentCache raised 32 -> 512 (12 KB) and AllocPermanent refuses allocations past the array instead of corrupting memory. - Same unbounded-index disease fixed in the three sibling writers: AllocCached's persistent path (16-entry array; CACHE_EITHER degrades to temporary, hard persistent requests fail cleanly), AllocPersistentSampleCacheEntry, AllocTemporarySampleCacheEntry. - seqLoadStatus malloc sized for the full id space (sequenceMapSize + 0xF) matching sequenceMap; custom ids above sequenceMapSize previously overflowed the allocation by up to 15 bytes. Upstream SoH bugs, not branch-introduced - this branch's many-track packs merely made the overflow reachable in normal play. Standalone upstreamable fix.
Diffstat (limited to 'soh/src/code/audio_load.c')
-rw-r--r--soh/src/code/audio_load.c5
1 files changed, 3 insertions, 2 deletions
diff --git a/soh/src/code/audio_load.c b/soh/src/code/audio_load.c
index 582cf3a1c..fcebb7104 100644
--- a/soh/src/code/audio_load.c
+++ b/soh/src/code/audio_load.c
@@ -1350,8 +1350,9 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
// calloc: unassigned slots stay NULL for the guard in AudioLoad_SyncInitSeqPlayerInternal().
sequenceMap = calloc(sequenceMapSize + 0xF, sizeof(char*));
- gAudioContext.seqLoadStatus = malloc(sequenceMapSize);
- memset(gAudioContext.seqLoadStatus, 5, sequenceMapSize);
+ // SOH [Bugfix] Size to match sequenceMap (+ 0xF); custom ids can exceed sequenceMapSize.
+ gAudioContext.seqLoadStatus = malloc(sequenceMapSize + 0xF);
+ memset(gAudioContext.seqLoadStatus, 5, sequenceMapSize + 0xF);
for (size_t i = 0; i < seqListSize; i++) {
SequenceData sDat = ResourceMgr_LoadSeqByName(seqList[i]);
sequenceMap[sDat.seqNumber] = strdup(seqList[i]);