Skip to content

fix(memory): harden read_ptr_unsafe under ASan, stabilize concurrent guarded-read tests#120

Merged
tkhquang merged 2 commits into
mainfrom
fix/read-ptr-unsafe-asan
Jun 13, 2026
Merged

fix(memory): harden read_ptr_unsafe under ASan, stabilize concurrent guarded-read tests#120
tkhquang merged 2 commits into
mainfrom
fix/read-ptr-unsafe-asan

Conversation

@tkhquang

@tkhquang tkhquang commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Summary

The AddressSanitizer probe was failing two MemoryTest cases. Both are addressed here, and the guard itself is shown correct rather than worked around.

read_ptr_unsafe kept a private __try/std::memcpy that never gained the hardening seh_read_bytes received: the low/null source floor, the (src + size) wrap rejection, and the __movsb copy that bypasses the MSVC ASan memcpy interceptor. Under ASan a wrapping source range aborted the process (negative-size-param) before SEH could turn it into a 0 return, and foreign reads were mis-handled by the interceptor. It now delegates to seh_read_bytes, so a single implementation owns every guarded-read safety property and the two cannot diverge again. The MinGW path is behaviourally unchanged (it already routed through the same fault guard).

The two concurrent guarded-read tests obtained a non-readable address by releasing a region with MEM_RELEASE, then read it from many threads expecting the guard to fault to 0. A released VA can be recycled and remapped by the allocations that spawning the reader threads triggers (thread stacks/TEBs, more so under ASan), so a read could land on live memory and the assertion flaked on roughly one read in many thousands. They now hold a committed PAGE_NOACCESS page until teardown, which faults deterministically and cannot be recycled, so they exercise the guard's per-thread fault isolation without that artifact. AGENTS.md documents the pitfall so it is not reintroduced.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Enhanced stability of memory read operations during concurrent execution by improving fault isolation handling.
  • Tests

    • Refined concurrency test robustness by eliminating nondeterminism from memory allocation patterns.

tkhquang added 2 commits June 13, 2026 12:02
…entry point

read_ptr_unsafe kept a private __try/std::memcpy that never gained the safety properties seh_read_bytes did: the low/null source floor, the (src + size) wrap rejection, and the __movsb copy that avoids MSVC routing std::memcpy through the ASan interceptor. Under AddressSanitizer a wrapping source range aborted the process (negative-size-param) before SEH could turn it into a 0 return, and foreign reads were mis-handled by the interceptor. Delegate to seh_read_bytes so a single implementation owns every guarded-read safety property and the two cannot diverge again.
… tests

GuardedReadsAreThreadIsolatedUnderConcurrency and GuardedReadsSurviveConcurrentShutdown obtained a non-readable address by MEM_RELEASE'ing a region, then read it from many threads expecting the guard to fault to 0. A released VA can be recycled and remapped by the allocations that spawning those threads triggers (thread stacks/TEBs, more so under AddressSanitizer), so a read could land on live memory and the assertion flaked on about one read in many thousands. Hold a committed PAGE_NOACCESS page until teardown instead: it faults deterministically and cannot be recycled. Document the pitfall in AGENTS.md.
@tkhquang tkhquang self-assigned this Jun 13, 2026
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4b3a5c1c-b65e-447a-8e31-25ac2571bd07

📥 Commits

Reviewing files that changed from the base of the PR and between 2375e07 and 5808d01.

📒 Files selected for processing (3)
  • AGENTS.md
  • src/memory.cpp
  • tests/test_memory.cpp

📝 Walkthrough

Walkthrough

This PR refactors DetourModKit::Memory::read_ptr_unsafe to route guarded pointer-sized reads through the unified seh_read_bytes implementation, updates two concurrency tests to use deterministic PAGE_NOACCESS pages instead of MEM_RELEASEd memory, and documents the faulting pattern in AGENTS.md guidance.

Changes

Guarded-read unification and test stabilization

Layer / File(s) Summary
Unified guarded-read implementation
src/memory.cpp
read_ptr_unsafe is refactored to perform pointer-sized reads via the shared seh_read_bytes implementation instead of separate MSVC __try/__except and MinGW veh_read_bytes code paths, centralizing safety semantics while preserving failure-returns-zero behavior.
Deterministic concurrency test updates
tests/test_memory.cpp
Both GuardedReadsAreThreadIsolatedUnderConcurrency and GuardedReadsSurviveConcurrentShutdown replace MEM_RELEASEd page allocations with committed PAGE_NOACCESS pages and update reader logic and cleanup to target the committed page, eliminating address-recycling nondeterminism.
Concurrency testing guidance
AGENTS.md
Testing guidance is updated to require that concurrency tests needing a non-readable address retain a committed PAGE_NOACCESS page through teardown to avoid flakiness from virtual-address recycling under AddressSanitizer.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • tkhquang/DetourModKit#103: Modifies seh_read_bytes with ASan-specific fault probing changes; directly intersects with the main PR's routing of read_ptr_unsafe through seh_read_bytes.
  • tkhquang/DetourModKit#113: Refactors memory.cpp to centralize guarded-read fault filtering for SEH paths, directly related to the main PR's consolidation of read_ptr_unsafe under seh_read_bytes.
  • tkhquang/DetourModKit#118: Implements MinGW VEH-backed seh_read_bytes; provides the foundation for the main PR's refactoring of read_ptr_unsafe to use the shared guarded-read path.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: hardening read_ptr_unsafe under AddressSanitizer and stabilizing concurrent guarded-read tests, which directly match the primary objectives and file changes in the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tkhquang tkhquang merged commit 6da6455 into main Jun 13, 2026
6 checks passed
@tkhquang tkhquang deleted the fix/read-ptr-unsafe-asan branch June 13, 2026 05:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant