Summary
OpenALAudioManager::update() can dereference a dangling AudioEventRTS, causing a SIGSEGV ~30–60s into a session (reproducible on Apple Silicon / macOS 26, weekly-2026-05-29 ZH build). Most visible when launching with -noaudio, but the underlying lifetime bug is independent of the audio-on state.
Crash (symbolicated, arm64)
AsciiString::str()
operator==(AsciiString const&, AsciiString const&)
AudioEventRTS::getAudioEventInfo() const
OpenALAudioManager::doesViolateLimit(AudioEventRTS*) / adjustPlayingVolume(PlayingAudio*)
OpenALAudioManager::processPlayingList() / SoundManager::canPlayNow()
OpenALAudioManager::update()
Faulting read is on a freed string (the bad address is the ASCII bytes of an event name read as a pointer).
Root cause
releasePlayingAudio() frees the event via releaseAudioEventRTS(release->m_audioEventRTS) when m_cleanupAudioEventRTS is set. When a single AudioEventRTS is shared (e.g. across Attack/Sound/Decay portions or 2D/3D request+playing entries), freeing it through one PlayingAudio leaves other PlayingAudio entries with a dangling m_audioEventRTS. The existing if (!m_audioEventRTS) guards don't catch a freed-but-non-null pointer, so the next frame's getAudioEventInfo() reads freed memory.
Mitigation (verified — stops the crash)
Skip list processing in update() when all audio affects are off (the common -noaudio path). Verified: rebuilt & ran with no audio crash.
void OpenALAudioManager::update() {
AudioManager::update();
if (!isOn(AudioAffect_Music) && !isOn(AudioAffect_Sound)
&& !isOn(AudioAffect_Sound3D) && !isOn(AudioAffect_Speech)) {
return;
}
setDeviceListenerPosition();
processRequestList(); processPlayingList(); processFadingList(); processStoppedList();
}
This is a mitigation, not the root fix — the proper fix is ownership/lifetime of the shared AudioEventRTS (ensure only the last reference frees it, or null out sibling references on free). Patch attached. Happy to open a PR if useful.
Environment: M4 Pro, macOS 26.x, GeneralsXZH weekly-2026-05-29, retail 1.04 data.
Summary
OpenALAudioManager::update()can dereference a danglingAudioEventRTS, causing aSIGSEGV~30–60s into a session (reproducible on Apple Silicon / macOS 26, weekly-2026-05-29 ZH build). Most visible when launching with-noaudio, but the underlying lifetime bug is independent of the audio-on state.Crash (symbolicated, arm64)
Faulting read is on a freed string (the bad address is the ASCII bytes of an event name read as a pointer).
Root cause
releasePlayingAudio()frees the event viareleaseAudioEventRTS(release->m_audioEventRTS)whenm_cleanupAudioEventRTSis set. When a singleAudioEventRTSis shared (e.g. across Attack/Sound/Decay portions or 2D/3D request+playing entries), freeing it through onePlayingAudioleaves otherPlayingAudioentries with a danglingm_audioEventRTS. The existingif (!m_audioEventRTS)guards don't catch a freed-but-non-null pointer, so the next frame'sgetAudioEventInfo()reads freed memory.Mitigation (verified — stops the crash)
Skip list processing in
update()when all audio affects are off (the common-noaudiopath). Verified: rebuilt & ran with no audio crash.This is a mitigation, not the root fix — the proper fix is ownership/lifetime of the shared
AudioEventRTS(ensure only the last reference frees it, or null out sibling references on free). Patch attached. Happy to open a PR if useful.Environment: M4 Pro, macOS 26.x, GeneralsXZH weekly-2026-05-29, retail 1.04 data.