src/internal/hook_patch_witness.hpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef DETOURMODKIT_INTERNAL_HOOK_PATCH_WITNESS_HPP | ||
| 2 | #define DETOURMODKIT_INTERNAL_HOOK_PATCH_WITNESS_HPP | ||
| 3 | |||
| 4 | /** | ||
| 5 | * @file hook_patch_witness.hpp | ||
| 6 | * @brief Four-state ownership witness for inline-hook patch windows. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "internal/hook_fault_boundary.hpp" | ||
| 10 | #include "internal/memory_guarded.hpp" | ||
| 11 | |||
| 12 | #include <algorithm> | ||
| 13 | #include <array> | ||
| 14 | #include <cstddef> | ||
| 15 | #include <cstdint> | ||
| 16 | #include <string_view> | ||
| 17 | |||
| 18 | namespace DetourModKit::detail | ||
| 19 | { | ||
| 20 | /** | ||
| 21 | * @brief Who owns the bytes currently at an inline backend's patch window. | ||
| 22 | * @details Original means no patch is installed; OwnedPatch means the bytes exactly match an encoding this backend | ||
| 23 | * committed; Foreign means the readable window matches neither; Indeterminate means it could not be read. | ||
| 24 | */ | ||
| 25 | enum class PatchWitness : std::uint8_t | ||
| 26 | { | ||
| 27 | Original, | ||
| 28 | OwnedPatch, | ||
| 29 | Foreign, | ||
| 30 | Indeterminate | ||
| 31 | }; | ||
| 32 | |||
| 33 | /// Names a patch witness for an operator-facing diagnostic. | ||
| 34 | 43 | [[nodiscard]] constexpr std::string_view witness_description(PatchWitness witness) noexcept | |
| 35 | { | ||
| 36 |
4/5✓ Branch 2 → 3 taken 4 times.
✓ Branch 2 → 4 taken 22 times.
✓ Branch 2 → 5 taken 12 times.
✓ Branch 2 → 6 taken 5 times.
✗ Branch 2 → 7 not taken.
|
43 | switch (witness) |
| 37 | { | ||
| 38 | 4 | case PatchWitness::Original: | |
| 39 | 4 | return "prologue is original"; | |
| 40 | 22 | case PatchWitness::OwnedPatch: | |
| 41 | 22 | return "our patch is still installed"; | |
| 42 | 12 | case PatchWitness::Foreign: | |
| 43 | 12 | return "another writer owns the prologue"; | |
| 44 | 5 | case PatchWitness::Indeterminate: | |
| 45 | 5 | break; | |
| 46 | } | ||
| 47 | 5 | return "prologue could not be read"; | |
| 48 | } | ||
| 49 | |||
| 50 | /** | ||
| 51 | * @brief Reads a backend target and classifies ownership of its patch window. | ||
| 52 | * @details OwnedPatch requires both exact bytes and persistent provenance that the backend completed a patch-byte | ||
| 53 | * capture. Pre-sized or default-initialized storage is not evidence that the backend emitted those bytes. | ||
| 54 | */ | ||
| 55 | 17341 | template <class Backend> [[nodiscard]] PatchWitness witness_patch(const Backend &backend) noexcept | |
| 56 | { | ||
| 57 | 17341 | const auto &original = backend.original_bytes(); | |
| 58 | 17341 | std::array<std::uint8_t, BACKEND_MAX_STEAL_WINDOW> current{}; | |
| 59 |
8/16DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::InlineHook>(safetyhook::InlineHook const&):
✓ Branch 4 → 5 taken 16743 times.
✗ Branch 4 → 11 not taken.
✓ Branch 8 → 9 taken 16743 times.
✗ Branch 8 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 16743 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 16743 times.
DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::MidHook>(safetyhook::MidHook const&):
✓ Branch 4 → 5 taken 598 times.
✗ Branch 4 → 11 not taken.
✓ Branch 8 → 9 taken 598 times.
✗ Branch 8 → 11 not taken.
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 598 times.
✗ Branch 13 → 14 not taken.
✓ Branch 13 → 15 taken 598 times.
|
34682 | if (original.empty() || original.size() > current.size() || backend.target() == nullptr) |
| 60 | { | ||
| 61 | ✗ | return PatchWitness::Indeterminate; | |
| 62 | } | ||
| 63 | 17341 | const std::size_t count = original.size(); | |
| 64 |
4/4DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::InlineHook>(safetyhook::InlineHook const&):
✓ Branch 20 → 21 taken 7 times.
✓ Branch 20 → 22 taken 16736 times.
DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::MidHook>(safetyhook::MidHook const&):
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 22 taken 597 times.
|
17341 | if (!guarded_read_bytes(reinterpret_cast<std::uintptr_t>(backend.target()), current.data(), count)) |
| 65 | { | ||
| 66 | 8 | return PatchWitness::Indeterminate; | |
| 67 | } | ||
| 68 | 17333 | const auto span = static_cast<std::ptrdiff_t>(count); | |
| 69 |
4/4DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::InlineHook>(safetyhook::InlineHook const&):
✓ Branch 29 → 30 taken 8100 times.
✓ Branch 29 → 31 taken 8636 times.
DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::MidHook>(safetyhook::MidHook const&):
✓ Branch 29 → 30 taken 464 times.
✓ Branch 29 → 31 taken 133 times.
|
34666 | if (std::equal(original.begin(), original.begin() + span, current.begin())) |
| 70 | { | ||
| 71 | 8564 | return PatchWitness::Original; | |
| 72 | } | ||
| 73 | 8769 | const auto &emitted = backend.patch_bytes(); | |
| 74 |
6/8DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::InlineHook>(safetyhook::InlineHook const&):
✓ Branch 33 → 34 taken 8633 times.
✓ Branch 33 → 45 taken 3 times.
✓ Branch 35 → 36 taken 8633 times.
✗ Branch 35 → 45 not taken.
DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::MidHook>(safetyhook::MidHook const&):
✓ Branch 33 → 34 taken 132 times.
✓ Branch 33 → 45 taken 1 time.
✓ Branch 35 → 36 taken 132 times.
✗ Branch 35 → 45 not taken.
|
17534 | if (backend.patch_bytes_valid() && emitted.size() == count && |
| 75 |
8/8DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::InlineHook>(safetyhook::InlineHook const&):
✓ Branch 43 → 44 taken 8610 times.
✓ Branch 43 → 45 taken 23 times.
✓ Branch 46 → 47 taken 8610 times.
✓ Branch 46 → 48 taken 26 times.
DetourModKit::detail::PatchWitness DetourModKit::detail::witness_patch<safetyhook::MidHook>(safetyhook::MidHook const&):
✓ Branch 43 → 44 taken 131 times.
✓ Branch 43 → 45 taken 1 time.
✓ Branch 46 → 47 taken 131 times.
✓ Branch 46 → 48 taken 2 times.
|
26299 | std::equal(emitted.begin(), emitted.begin() + span, current.begin())) |
| 76 | { | ||
| 77 | 8741 | return PatchWitness::OwnedPatch; | |
| 78 | } | ||
| 79 | 28 | return PatchWitness::Foreign; | |
| 80 | } | ||
| 81 | |||
| 82 | /// Returns whether a witness authorizes the backend to overwrite the target. | ||
| 83 | 8633 | [[nodiscard]] constexpr bool witness_permits_write(PatchWitness witness) noexcept | |
| 84 | { | ||
| 85 |
4/4✓ Branch 2 → 3 taken 4244 times.
✓ Branch 2 → 4 taken 4389 times.
✓ Branch 3 → 4 taken 4221 times.
✓ Branch 3 → 5 taken 23 times.
|
8633 | return witness == PatchWitness::Original || witness == PatchWitness::OwnedPatch; |
| 86 | } | ||
| 87 | } // namespace DetourModKit::detail | ||
| 88 | |||
| 89 | #endif // DETOURMODKIT_INTERNAL_HOOK_PATCH_WITNESS_HPP | ||
| 90 |