Preserve SafetyHook concurrency finding as a committed upstream repro

A multithreaded test that tight-looped InlineHook enable()/disable() while
other threads called the hooked function flaked ~1/10. Isolation proved this is
a SafetyHook limitation, not our code: with the hook created once (no install
race, no trampoline UAF), tight-loop toggling AVs ~1/3 of runs in Debug
(0xC0000005, faulting RIP in the target body), while a no-toggle control is
clean at ~60M calls. enable()/disable() re-patch the prologue in place under a
VEH page-trap that only relocates a thread parked ON the prologue; a thread in
the function body faults on the briefly-non-exec page and relies on instruction
retry, which under rapid toggling races a half-rewritten prologue.

Rather than silently drop the flaky test, preserve the finding:
- tools/sh_concurrency_repro/: minimal, committed, non-CI reproducer
  (coop_sh_concurrency_repro; --callonly is the control). Surfaces 5/16 AVs.
- docs/safetyhook-concurrency.md: upstream-ready write-up (mechanism + fix
  directions + why it does not affect us).
- README lessons-learned + memory updated; tests/CMakeLists cross-references it.

Our code stays in SafetyHook's safe envelope (install/remove reconciled from a
single tick-bounded worker thread, never a tight loop), so the mock_game_test
storm is reliably green; the persistent-trampoline contract is covered
deterministically by hook_install_test + detour_gate_test. Removes the temp
_sh_probe wiring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-23 12:23:06 +02:00
parent f843c56f5b
commit 51e2c4f4cd
6 changed files with 213 additions and 0 deletions

View File

@@ -48,6 +48,14 @@ add_test(NAME detour_gate_test COMMAND detour_gate_test)
# Deterministic contract test for the persistent inline-hook model (hook/src/hook_install.hpp):
# install_inline creates a hook once and re-enables it on re-install, reusing the trampoline (never
# freed) so a stale detour can't UAF. Single-threaded, so it can't flake on enable/disable atomicity.
#
# A multithreaded TIGHT-LOOP toggle test used to live here and flaked ~1/10: it was asserting an
# atomicity that SafetyHook's enable()/disable() don't provide and that our code never needs (we
# reconcile install/remove from a single, tick-bounded worker thread -- never a tight loop). That
# valid contract is now covered deterministically here + by detour_gate_test; the realistic concurrent
# behaviour by mock_game_test's storm; and the SafetyHook limitation itself is captured as a committed
# upstream reproducer at tools/sh_concurrency_repro/ (see docs/safetyhook-concurrency.md) rather than a
# flaky CTest.
add_executable(hook_install_test hook_install_test.cpp)
target_include_directories(hook_install_test PRIVATE ${CMAKE_SOURCE_DIR}/hook/src)
target_link_libraries(hook_install_test PRIVATE safetyhook::safetyhook)