steam-audio icon indicating copy to clipboard operation
steam-audio copied to clipboard

[Wwise & UE 5.5] Crash in Wwise Integration: Use-After-Free due to Race Condition on Source Destruction

Open Aper-mesa opened this issue 5 months ago • 1 comments

A fatal EXCEPTION_ACCESS_VIOLATION crash occurs when an Actor with a UAkComponent and a USteamAudioSourceComponent is destroyed while its sound is still playing or fading out in the Wwise Sound Engine. The crash originates from within the phonon library, indicating that it's trying to access a null or invalid pointer. Call Stack: code Code Unhandled Exception: EXCEPTION_ACCESS_VIOLATION reading address 0x0000000000000010

phonon phonon SteamAudioWwise SteamAudioWwise UnrealEditor_WwiseSoundEngine!CAkVPLFilterNodeOutOfPlace::Init() UnrealEditor_WwiseSoundEngine!CAkVPLSrcCbxNode::CreateEffects() ... (and so on)

Root Cause Analysis:

The crash is caused by a race condition between the Unreal Engine game thread and the Wwise audio thread. When an Actor is destroyed, FWwiseAudioEngineSource::Destroy() is called on the game thread. This function immediately calls iplWwiseRemoveSource(GameObjectID). This removes the mapping between the Wwise GameObjectID and the IPLSource from within the Wwise plug-in. However, the Wwise audio thread, which runs asynchronously, may not have finished processing the sound associated with that GameObjectID. The sound could still be in its release phase or fading out. On its next processing tick, the audio thread attempts to render the Steam Audio effect for the sound. It requests the IPLSource for the GameObjectID, but since the mapping was already removed, it receives a NULL pointer. This NULL pointer is then passed down to the phonon library, which attempts to dereference it (e.g., access a member at an offset, like 0x10), causing the access violation crash. This is a classic use-after-free problem, where a resource is de-registered before all users have finished with it. The Destroy() function in SteamAudioWwise.cpp is the trigger for this unsafe sequence of events.

Proposed Solution

To resolve this, the AkComponent's sound must be explicitly stopped before the Steam Audio source is removed. This helps synchronize the state between the game and audio threads. The following change to FWwiseAudioEngineSource::Destroy() in SteamAudioWwise.cpp resolves the crash: code C++

// In FWwiseAudioEngineSource::Destroy()

void FWwiseAudioEngineSource::Destroy()
{
	if (GameObjectID == AK_INVALID_GAME_OBJECT)
		return;

    // --- PROPOSED FIX ---
    // Ensure the AkComponent is valid and explicitly stop the sound.
    // This sends a command to the audio thread to terminate the sound instance,
    // preventing it from trying to access Steam Audio data after the source is removed.
    if (AkComponent && IsValid(AkComponent)) 
    {
        AkComponent->Stop();
    }
    // --- END FIX ---

	FSteamAudioWwiseModule::Get().iplWwiseRemoveSource(GameObjectID);
    GameObjectID = AK_INVALID_GAME_OBJECT;
}

Explanation of the Fix:

By calling AkComponent->Stop(), we post a stop event to the Wwise audio engine's command queue. This ensures that the sound instance is terminated gracefully in the audio thread. By the time iplWwiseRemoveSource is called, it is highly likely that the audio thread is no longer attempting to process effects for this GameObjectID, thus preventing the null pointer access and the subsequent crash. This change establishes a safer shutdown sequence: 1) Request sound termination, 2) De-register associated resources. This fix has been tested and confirmed to solve the stability issue. I suggest incorporating this or a similar logic into the official plugin source code. Thank you

P.S.: This bug report was generated by Gemini 2.5 Pro. For any mistakes or misunderstandings, please reply below. I also encourage you guys to update SteamAudioWwise plugin to support newer WWise versions, UE fails to build for Wwise 2024.1.5 or newer.

Aper-mesa avatar Sep 12 '25 12:09 Aper-mesa

@Aper-mesa Could you provide a crash dump or minimal project that reproduces the issue? I'm looking at the code around this, and the proposed fix, and my concern is that since AkComponent->Stop() doesn't synchronously stop the sound, it's just reducing the probability of a crash, without fully fixing it.

Also, the spatializer effect holds a shared_ptr to a double-buffered IPLSource, and calling iplWwiseRemoveSource just releases the shared_ptr held in the global state, which shouldn't affect the reference stored in the spatializer effect, so I wonder if there is something else going on here. A crash dump (or minimal project) will help diagnose the issue.

lakulish avatar Oct 06 '25 14:10 lakulish