From 8dc03cedf0995d8a74d0f834cfd6af7aa7266591 Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Mon, 25 May 2026 09:14:44 +0800 Subject: [PATCH 1/3] fix: avoid loader-lock work in DllMain detach --- src/dllmain.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/dllmain.cpp b/src/dllmain.cpp index 1a08004..d5157a0 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -83,22 +83,22 @@ static DWORD WINAPI InitThread(LPVOID param) { return 0; } -BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved) -{ - if (dwReason == DLL_PROCESS_ATTACH) - { - DisableThreadLibraryCalls(hModule); +BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hModule); // Hand off all real work to a worker thread to avoid running file I/O, // LoadLibrary, and detour transactions under the loader lock. HANDLE h = CreateThread(nullptr, 0, InitThread, hModule, 0, nullptr); if (h) CloseHandle(h); - } - else if (dwReason == DLL_PROCESS_DETACH) - { - FileWatcher::Stop(); - SteamUI::CoreUnhook(); - SteamClient::CoreUnhook(); - } - - return TRUE; -} + } + else if (dwReason == DLL_PROCESS_DETACH) + { + // DllMain runs under loader lock. Blocking joins or Detours transactions + // here can deadlock the process during shutdown/unload. + (void)pvReserved; + } + + return TRUE; +} From 468d8f54c70fbc862df09ad2388de5489c98a51f Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Mon, 25 May 2026 09:30:45 +0800 Subject: [PATCH 2/3] fix: add non-blocking watcher cleanup on explicit unload --- src/Utils/FileWatcher.cpp | 7 +++++++ src/Utils/FileWatcher.h | 9 +++++---- src/dllmain.cpp | 10 +++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/Utils/FileWatcher.cpp b/src/Utils/FileWatcher.cpp index 1b5b061..9e70a36 100644 --- a/src/Utils/FileWatcher.cpp +++ b/src/Utils/FileWatcher.cpp @@ -214,4 +214,11 @@ namespace FileWatcher { g_watcherThread.join(); } } + + void StopNoJoin() { + g_running = false; + if (g_watcherThread.joinable()) { + g_watcherThread.detach(); + } + } } diff --git a/src/Utils/FileWatcher.h b/src/Utils/FileWatcher.h index e4ee75a..3dd7fc9 100644 --- a/src/Utils/FileWatcher.h +++ b/src/Utils/FileWatcher.h @@ -4,7 +4,8 @@ #include #include -namespace FileWatcher { - void Start(const std::vector& directories); - void Stop(); -} \ No newline at end of file +namespace FileWatcher { + void Start(const std::vector& directories); + void Stop(); + void StopNoJoin(); +} diff --git a/src/dllmain.cpp b/src/dllmain.cpp index d5157a0..a646995 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -95,9 +95,13 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved) } else if (dwReason == DLL_PROCESS_DETACH) { - // DllMain runs under loader lock. Blocking joins or Detours transactions - // here can deadlock the process during shutdown/unload. - (void)pvReserved; + // DLL detach runs under loader lock. Never block here. + // For explicit FreeLibrary (pvReserved == nullptr), best-effort + // signal the watcher thread to exit and detach the std::thread object + // so module teardown does not hit std::thread destructor terminate. + if (pvReserved == nullptr) { + FileWatcher::StopNoJoin(); + } } return TRUE; From 7c43c3f7cdf9585f8450dff073c3e9c18da8549c Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Mon, 25 May 2026 09:35:34 +0800 Subject: [PATCH 3/3] fix: pin module and stop watcher on process detach --- src/Utils/FileWatcher.cpp | 7 ------- src/Utils/FileWatcher.h | 1 - src/dllmain.cpp | 24 ++++++++++++++---------- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Utils/FileWatcher.cpp b/src/Utils/FileWatcher.cpp index 9e70a36..1b5b061 100644 --- a/src/Utils/FileWatcher.cpp +++ b/src/Utils/FileWatcher.cpp @@ -214,11 +214,4 @@ namespace FileWatcher { g_watcherThread.join(); } } - - void StopNoJoin() { - g_running = false; - if (g_watcherThread.joinable()) { - g_watcherThread.detach(); - } - } } diff --git a/src/Utils/FileWatcher.h b/src/Utils/FileWatcher.h index 3dd7fc9..488511b 100644 --- a/src/Utils/FileWatcher.h +++ b/src/Utils/FileWatcher.h @@ -7,5 +7,4 @@ namespace FileWatcher { void Start(const std::vector& directories); void Stop(); - void StopNoJoin(); } diff --git a/src/dllmain.cpp b/src/dllmain.cpp index a646995..cef2243 100644 --- a/src/dllmain.cpp +++ b/src/dllmain.cpp @@ -88,19 +88,23 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved) if (dwReason == DLL_PROCESS_ATTACH) { DisableThreadLibraryCalls(hModule); - // Hand off all real work to a worker thread to avoid running file I/O, - // LoadLibrary, and detour transactions under the loader lock. - HANDLE h = CreateThread(nullptr, 0, InitThread, hModule, 0, nullptr); - if (h) CloseHandle(h); + // Keep this module pinned so explicit FreeLibrary cannot unload code + // while hooks and worker threads may still reference it. + HMODULE pinnedModule = nullptr; + GetModuleHandleExA( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, + reinterpret_cast(&DllMain), &pinnedModule); + // Hand off all real work to a worker thread to avoid running file I/O, + // LoadLibrary, and detour transactions under the loader lock. + HANDLE h = CreateThread(nullptr, 0, InitThread, hModule, 0, nullptr); + if (h) CloseHandle(h); } else if (dwReason == DLL_PROCESS_DETACH) { - // DLL detach runs under loader lock. Never block here. - // For explicit FreeLibrary (pvReserved == nullptr), best-effort - // signal the watcher thread to exit and detach the std::thread object - // so module teardown does not hit std::thread destructor terminate. - if (pvReserved == nullptr) { - FileWatcher::StopNoJoin(); + // During process termination, join watcher thread object so CRT static + // teardown does not hit std::thread's joinable-guard terminate. + if (pvReserved != nullptr) { + FileWatcher::Stop(); } }