Detach RED4ext hooks at the top of App::Shutdown#151
Open
JustinMDotNet wants to merge 1 commit into
Open
Conversation
App::Shutdown cleared m_systems while RED4ext's own game-side detours were still attached. The detach lived in App::Destruct, which runs much later from DLL_PROCESS_DETACH. In the QuickExit path Destruct doesn't even run before the chained quick_exit fires, so the hooks stay live during teardown. In-flight detours on InitScripts, LoadScripts, and CGameApplication_AddState call back through App::Get()->GetXxxSystem(), which does m_systems.at(...) and throws std::out_of_range on an empty vector. No try/catch on the game side, so the throw becomes std::terminate and the user sees an unhandled-exception dialog on what should be a clean quit. Move the detach into a new App::DetachHooks() member, call it at the top of App::Shutdown before any system is torn down, and keep a defence-in-depth call in App::Destruct for the case where Shutdown was never reached. DetachHooks tracks attach/detach state in a new m_hooksAttached flag so the double-call from the normal path is a no-op. AttachHooks drops its const qualifier so it can set the flag on success. Fixes wopss#141 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Owner
|
Thanks for the PR! This PR introduce a To give a bit of background RED4ext is split into 2 life cycles:
Currently, you're proposing to detach the hooks in the second life cycle, which is STILL using the hooks RED4ext/src/dll/Hooks/QuickExit.cpp Line 22 in c52c8d8 quick_exit function RED4ext/src/dll/Hooks/QuickExit.cpp Line 35 in c52c8d8 If we are removing the hooks in this life cycle, then the memory region that can redirect from our hooked function to the real function will be freed. Besides this, I do not see any problem with the current implementation, because:
|
Owner
|
Is this proposed fix an issue you/someone had? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #141.
What this changes
Moves the detour-detach block from
App::Destructinto a newApp::DetachHooks()member that:App::Shutdown, before any system is shut down.App::Destructas a defence-in-depth no-op for the ungraceful-exit path.m_hooksAttachedflag thatAttachHookssets on success andDetachHooksclears.AttachHooksis no longerconstbecause it now setsm_hooksAttached.Why
In the original code,
_QuickExit_fnccalledapp->Shutdown()and then chained the originalquick_exit. Hooks remained installed for the rest of process teardown because the actual detach happened inApp::Destruct, which only runs fromDLL_PROCESS_DETACHlater.Three currently-installed hooks call back into
App::GetXxxSystem()from their detour bodies:Hooks/InitScripts.cpp:27→App::Get()->GetScriptCompilationSystem()Hooks/LoadScripts.cpp:20→ sameHooks/CGameApplication.cpp:19-77→ indirectly viaStates::*::AttachGetXxxSystem()doesm_systems.at(...)(App.cpp:182-210), which throwsstd::out_of_rangeon an empty vector. AfterShutdownranm_systems.clear();, an in-flight detour on another thread (or a queued continuation on the same thread) would throw straight into game code. Notry/catchon the game side →std::terminate, ugly exit dialog, "RED4ext crashed the game on quit" report.Detaching the hooks first closes that window. Once
DetachHooks()returns, the trampolines are gone and the game code runs unmodified for the rest of teardown.Design choice
Picked the "detach hooks first" option from the issue rather than "make accessors null-safe" because it requires no changes to any hook body and it preserves the existing contract that
GetXxxSystem()returns a valid pointer. Making accessors null-safe would also be defensible defence-in-depth, but it would mean every detour body has to handle a null system gracefully, which is invasive.The defence-in-depth
DetachHooks()call inDestructpreserves the original behaviour for the case whereShutdownwas never called (e.g.DLL_PROCESS_DETACHwithaReserved == nullptrand no precedingQuickExit). The idempotency check viam_hooksAttachedmakes the double-call from the normal path safe.What I tested
Local rebuild. The flow under each path:
QuickExit→app->Shutdown()→DetachHooks()(commits,m_hooksAttached = false) → systems shut down in reverse →m_systems.clear()→ eventuallyApp::Destruct→DetachHooks()(no-op).App::Destruct→DetachHooks()(commits,m_hooksAttached = false) →g_app.reset()→ systems destroyed viaunique_ptr, matching the original behavior.aReserved != nullptr: skipped entirely per the existing guard inMain.cpp:39-44.