feat: add InjectorPP::new_global() for cross-thread fake visibility#125
Merged
mazong1123 merged 3 commits intomainfrom Mar 27, 2026
Merged
feat: add InjectorPP::new_global() for cross-thread fake visibility#125mazong1123 merged 3 commits intomainfrom
mazong1123 merged 3 commits intomainfrom
Conversation
Add a new constructor InjectorPP::new_global() that uses direct code patching (JMP instruction overwrite) instead of thread-local dispatch. This makes fakes visible to all threads, which is needed when faked functions are called from background threads (e.g., timer callbacks). Key changes: - Add ew_global() constructor using PatchGuard (direct patching) - ew() continues to use thread-local dispatch (default, parallel-safe) - RwLock coordination: new() takes read lock (parallel), new_global() takes write lock (exclusive) to prevent interference - Resolve IAT thunks in PatchGuard path to match TLS dispatcher behavior - Make PatchGuard helpers available on all architectures in internal.rs The two modes: - new() [default]: Thread-local, parallel-safe, fakes only on creator thread - new_global(): Global patching, serialized, fakes visible to all threads Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… gate - Replace || func() closures with func references in test spawns (clippy) - Gate thread-local isolation test with #[cfg(x86_64, aarch64)] since arm uses global patching for new() and has no TLS dispatch Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On ARM, PatchGuard uses 12-byte patches. Tiny helper functions (e.g.,
'fn f() -> i32 { 42 }') can be as small as 4-6 bytes in Thumb mode.
When two adjacent functions are patched simultaneously in the same
injector, the 12-byte patches overlap, corrupting the first patch's
target address and causing a SIGSEGV.
Fix: wrap return values in core::hint::black_box() to ensure each
function compiles to at least 16 bytes on all architectures.
Also re-enable thread-local isolation test on ARM (target_arch = arm
has TLS dispatch support).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new constructor
InjectorPP::new_global()that provides global fake visibility — fakes registered through this constructor are visible to all threads, not just the creating thread.This complements the default
InjectorPP::new()which uses thread-local dispatch for parallel test safety.Motivation
When faked functions are called from background threads (e.g., timer callbacks, thread pool workers), thread-local fakes are not visible because the background thread has no TLS entry.
new_global()solves this by using direct code patching (JMP instruction overwrite), making fakes visible process-wide.API
Both constructors expose the same
when_called()/will_execute()/will_return_boolean()API — the only difference is visibility scope.Design
new(): Thread-local dispatch via TLS HashMap. Multiple instances can run in parallel. Fakes only visible on the creating thread.new_global(): Direct JMP patching via PatchGuard. Acquires an exclusive write lock, so global-mode tests run serialized. Fakes visible to all threads.new()acquires a read lock (allows parallel TLS tests),new_global()acquires a write lock (waits for all other instances to drop, then holds exclusive access).Changes
src/interface/injector.rs: Addednew_global(),use_globalfield,RwGuardenum withGLOBAL_FAKE_LOCK: RwLocksrc/injector_core/internal.rs: Made PatchGuard helpers (will_execute_guard,will_return_boolean_guard) available on all architecturessrc/injector_core/patch_amd64.rs: Added IAT thunk resolution inpatch_and_guard()to match TLS dispatcher behaviorsrc/injector_core/thread_local_registry.rs: Removed unused global replacement codetests/global.rs: 10 new integration tests covering cross-thread visibility, call counting, restore-on-drop, multi-thread, unchecked mode, and thread-local isolation