GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 75.0% 15 / 0 / 20
Functions: 100.0% 2 / 0 / 2
Branches: 50.0% 8 / 0 / 16

src/platform.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_PLATFORM_HPP
2 #define DETOURMODKIT_PLATFORM_HPP
3
4 #include <windows.h>
5
6 namespace DetourModKit::detail
7 {
8 /**
9 * @brief Checks if the current thread holds the Windows loader lock.
10 * @details Reads the loader-lock CRITICAL_SECTION pointer from the PEB at a well-known offset (0x110 on x64, 0xA0
11 * on x86) that has been stable from Windows XP through 11, then compares its OwningThread to the current
12 * thread id. Thread joins are unsafe while the loader lock is held (DllMain context), so this gate decides
13 * detach-vs-join across every subsystem teardown.
14 * @note The result is fail-safe. If the PEB or the loader-lock pointer cannot be read, or the pointer does not
15 * resolve to committed, readable memory (e.g. a future or foreign PEB layout shifted the offset), the
16 * function assumes the lock IS held and returns true. A spurious true only leaks a detached thread (bounded,
17 * with the module pinned); a spurious false would join a thread under the loader lock and deadlock the host
18 * on unload, so uncertainty must bias toward true.
19 * @return true if the current thread holds the loader lock, or if ownership cannot be determined.
20 */
21 1075 [[nodiscard]] inline bool is_loader_lock_held() noexcept
22 {
23 #ifdef _WIN64
24 1075 auto *peb = reinterpret_cast<char *>(__readgsqword(0x60));
25 1075 constexpr size_t LOADER_LOCK_OFFSET = 0x110;
26 #else
27 auto *peb = reinterpret_cast<char *>(__readfsdword(0x30));
28 constexpr size_t LOADER_LOCK_OFFSET = 0xA0;
29 #endif
30
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 1075 times.
1075 if (!peb)
31 return true;
32
33 1075 auto *cs = *reinterpret_cast<PCRITICAL_SECTION *>(peb + LOADER_LOCK_OFFSET);
34
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 8 taken 1075 times.
1075 if (!cs)
35 return true;
36
37 // Confirm the critical section lives in committed, readable memory before dereferencing OwningThread. A wrong
38 // LOADER_LOCK_OFFSET (foreign or future
39 // PEB layout) would otherwise read a bogus pointer and fault the host.
40 1075 constexpr DWORD READABLE_PROTECT = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ |
41 PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
42 1075 MEMORY_BASIC_INFORMATION mbi{};
43
1/2
✓ Branch 10 → 11 taken 1075 times.
✗ Branch 10 → 13 not taken.
2150 if (VirtualQuery(cs, &mbi, sizeof(mbi)) != sizeof(mbi) || mbi.State != MEM_COMMIT ||
44
4/8
✓ Branch 9 → 10 taken 1075 times.
✗ Branch 9 → 13 not taken.
✓ Branch 11 → 12 taken 1075 times.
✗ Branch 11 → 13 not taken.
✗ Branch 12 → 13 not taken.
✓ Branch 12 → 14 taken 1075 times.
✗ Branch 15 → 16 not taken.
✓ Branch 15 → 17 taken 1075 times.
2150 (mbi.Protect & READABLE_PROTECT) == 0 || (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) != 0)
45 {
46 return true;
47 }
48
49 1075 return cs->OwningThread == reinterpret_cast<HANDLE>(static_cast<uintptr_t>(GetCurrentThreadId()));
50 }
51
52 /**
53 * @brief Pins the current module to prevent unload while detached threads are still running.
54 * @details Uses GET_MODULE_HANDLE_EX_FLAG_PIN to permanently increment the module reference count. Safe to call
55 * from DllMain. This ensures that code pages and static data remain valid for detached threads during the
56 * FreeLibrary path.
57 * @return true if the module was successfully pinned, false on failure.
58 */
59 8 inline bool pin_current_module() noexcept
60 {
61 8 HMODULE pin = nullptr;
62
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 8 times.
8 if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
63 reinterpret_cast<LPCWSTR>(&pin_current_module), &pin))
64 {
65 OutputDebugStringA("DetourModKit: pin_current_module failed; "
66 "detached threads may access unmapped code.\n");
67 return false;
68 }
69 8 return true;
70 }
71
72 } // namespace DetourModKit::detail
73
74 #endif // DETOURMODKIT_PLATFORM_HPP
75