GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 80.0% 8 / 0 / 10
Functions: 100.0% 1 / 0 / 1
Branches: 66.7% 4 / 0 / 6

src/internal/scan_fault_seam.hpp
Line Branch Exec Source
1 #ifndef DETOURMODKIT_INTERNAL_SCAN_FAULT_SEAM_HPP
2 #define DETOURMODKIT_INTERNAL_SCAN_FAULT_SEAM_HPP
3
4 /**
5 * @file internal/scan_fault_seam.hpp
6 * @brief Test-only injection point that raises one access fault at a chosen address inside a guarded scanner body.
7 * @details The scanner's region sweep and string-xref window sweep run their foreign reads inside a fault guard that
8 * declares an exact span. Whether that guard screens the FAULTING ADDRESS, or merely the exception class, is
9 * not observable from outside: a fault at an address the span does not cover must reach the host's handlers,
10 * and the only way to drive one is from inside the guarded frame. This seam is that driver. The whole header
11 * is behind DMK_ENABLE_TEST_SEAMS, so a shipping archive carries neither the storage nor the fire site.
12 */
13
14 #if defined(DMK_ENABLE_TEST_SEAMS)
15
16 #include <atomic>
17 #include <cstdint>
18
19 namespace DetourModKit
20 {
21 namespace detail
22 {
23 /// Address the page-gated region sweep's guarded body reads once, or 0 when disarmed.
24 inline std::atomic<std::uintptr_t> g_scan_region_fault_for_test{0};
25
26 /// Address the string-xref narrow window body reads once, or 0 when disarmed.
27 inline std::atomic<std::uintptr_t> g_scan_window_fault_for_test{0};
28
29 /// Test callback invoked immediately before the narrow-window seam reads its armed address.
30 using ScanFaultPreparationForTest = void (*)(std::uintptr_t) noexcept;
31
32 /// Optional preparation that can reprotect the armed window address after the production gate has accepted it.
33 inline std::atomic<ScanFaultPreparationForTest> g_scan_window_fault_preparation_for_test{nullptr};
34
35 /**
36 * @brief Reads @p slot's armed address once through a volatile lvalue, then disarms the slot.
37 * @param slot The armed-address storage to consume.
38 * @param preparation Optional one-shot callback invoked before the read.
39 * @details Disarming before the access keeps a swept region that is visited more than once from raising a
40 * second fault the proof's oracle would not expect. The read is volatile so no optimizer may drop the
41 * dereference that is the entire point of the seam.
42 */
43 26341 inline void fire_scan_fault_seam_for_test(
44 std::atomic<std::uintptr_t> &slot,
45 std::atomic<ScanFaultPreparationForTest> *preparation = nullptr
46 ) noexcept
47 {
48 26341 const std::uintptr_t address = slot.exchange(0, std::memory_order_acq_rel);
49
2/2
✓ Branch 4 → 5 taken 26340 times.
✓ Branch 4 → 6 taken 1 time.
26341 if (address == 0)
50 {
51 26340 return;
52 }
53
1/2
✓ Branch 6 → 7 taken 1 time.
✗ Branch 6 → 10 not taken.
1 if (preparation != nullptr)
54 {
55 1 const ScanFaultPreparationForTest callback = preparation->exchange(nullptr, std::memory_order_acq_rel);
56
1/2
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 10 not taken.
1 if (callback != nullptr)
57 {
58 1 callback(address);
59 }
60 }
61 const auto *const probe = reinterpret_cast<volatile const std::uint8_t *>(address);
62 (void)*probe;
63 }
64 } // namespace detail
65 } // namespace DetourModKit
66
67 #endif // DMK_ENABLE_TEST_SEAMS
68
69 #endif // DETOURMODKIT_INTERNAL_SCAN_FAULT_SEAM_HPP
70