GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 91.3% 21 / 0 / 23
Functions: 100.0% 4 / 0 / 4
Branches: 60.0% 6 / 0 / 10

src/region.cpp
Line Branch Exec Source
1 #include "DetourModKit/region.hpp"
2
3 #include "internal/memory_guarded.hpp"
4 #include "internal/module_name.hpp"
5
6 #include <windows.h>
7
8 #include <cstddef>
9 #include <cstdint>
10 #include <string>
11
12 namespace DetourModKit
13 {
14 176 Region Region::host() noexcept
15 {
16 // GetModuleHandleW(nullptr) returns the base of the process image (the host .exe the mod DLL was injected
17 // into), which is the default scan scope for a cascade that carries no explicit range. The base is resolved
18 // to a full image span by the shared guarded PE resolver.
19 176 return detail::module_image_region(Address{::GetModuleHandleW(nullptr)});
20 }
21
22 7 Region Region::own() noexcept
23 {
24 // Resolve the module that contains this function's own code. Because DetourModKit is a static library, the
25 // address of Region::own lands inside whichever DLL/EXE linked it, so GetModuleHandleExW(FROM_ADDRESS, ...)
26 // returns that consumer module rather than the process EXE. FROM_ADDRESS plus UNCHANGED_REFCOUNT looks the
27 // handle up without taking a reference, matching the transient-scope contract every Region factory keeps.
28 7 HMODULE owning_module = nullptr;
29 7 if (!::GetModuleHandleExW(
30 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
31 reinterpret_cast<LPCWSTR>(&Region::own),
32 &owning_module
33
2/4
✓ Branch 3 → 4 taken 7 times.
✗ Branch 3 → 5 not taken.
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 7 times.
14 ) ||
34
1/2
✗ Branch 4 → 5 not taken.
✓ Branch 4 → 6 taken 7 times.
7 owning_module == nullptr)
35 {
36 return Region{};
37 }
38 7 return detail::module_image_region(Address{owning_module});
39 }
40
41 143 Region Region::module_named(std::string_view name) noexcept
42 {
43 // Invalid names and conversion failures take the same fail-closed path as an unloaded module.
44 143 const std::wstring wide_name = detail::widen_module_name(name);
45
2/2
✓ Branch 4 → 5 taken 2 times.
✓ Branch 4 → 7 taken 141 times.
143 if (wide_name.empty())
46 {
47 2 return Region{};
48 }
49
50 // GetModuleHandleW does not add a reference, so the returned handle is only valid while the module stays
51 // loaded. That is the correct contract here: a Region is a transient scan scope, not an ownership claim.
52 141 return detail::module_image_region(Address{::GetModuleHandleW(wide_name.c_str())});
53 143 }
54
55 9 Region Region::whole_process() noexcept
56 {
57 // Span the user-mode virtual-address window the OS reports for this process rather than hardcoding the
58 // canonical x64 0x7FFF'FFFFFFFF ceiling, so the Region stays correct regardless of address-layout settings.
59 9 SYSTEM_INFO system_info{};
60 9 ::GetSystemInfo(&system_info);
61
62 9 const auto minimum_address = reinterpret_cast<std::uintptr_t>(system_info.lpMinimumApplicationAddress);
63 9 const auto maximum_address = reinterpret_cast<std::uintptr_t>(system_info.lpMaximumApplicationAddress);
64
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 9 times.
9 if (maximum_address <= minimum_address)
65 {
66 return Region{};
67 }
68
69 // lpMaximumApplicationAddress is the highest address usable by the process and is INCLUSIVE, so the half-open
70 // Region must run one byte past it to actually contain that last address: size = (max - min) + 1. The guard
71 // above guarantees max > min, and an application maximum is never UINTPTR_MAX, so the + 1 cannot overflow.
72 9 return Region{Address{minimum_address}, static_cast<std::size_t>(maximum_address - minimum_address) + 1U};
73 }
74 } // namespace DetourModKit
75