Audio: fix five hook/unhook concurrency + over-write bugs

Found by the mock-game capture/audio/hook stress test (toggling the audio
subsystem while a game renders):

1. Guessed-stream silence over-WRITE: hk_ReleaseBuffer zeroed num_frames *
   guessed_block bytes, but a guess can be larger than the real per-frame size
   (e.g. an 8ch device guess for a 2ch game), so the memset wrote past the real
   buffer into adjacent audio memory -> intermittent access violation in the
   game. Fix: capture but do NOT silence a guessed stream (it stays audible --
   echo); only an exact/override format, whose frame size is known, gets the
   no-echo silence.
2. VtableHook::remove nulled m_original, racing an in-flight detour into a null
   call -> keep it valid (the original function stays mapped).
3. g_ipc was a non-atomic pointer read on the hot path while unhook nulled it
   (TOCTOU) -> make it atomic, load once.
4. Stale GetBuffer/ReleaseBuffer pairing across a toggle -> epoch-stamp the
   GetBuffer and only capture in the same hooked epoch.
5. COM-object churn: re-creating the probe client every enable raced AudioSes ->
   build the probe once, keep it across toggles (only swap vtable slots);
   release on detach (shutdown_audio_hooks). Plus drain in-flight detours before
   tearing down state.

Stress test: 0 crashes in many repeated runs (was ~50%). Guessed streams now
echo (the no-echo path is reached via an exact/auto-attach format or override).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 05:21:17 +02:00
parent 549b91a9ce
commit e2562ac63c
4 changed files with 195 additions and 79 deletions

View File

@@ -252,7 +252,7 @@ BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved)
coop::hook::set_log_ring(nullptr);
coop::hook::remove_focus_spoof();
coop::hook::remove_xinput_hooks();
coop::hook::remove_audio_hooks();
coop::hook::shutdown_audio_hooks(); // detach: remove detours + release the kept probe
coop::hook::remove_present_hooks();
coop::hook::remove_opengl_hooks();
coop::hook::remove_mkb_hooks();