diff --git a/crates/fspy_preload_unix/src/client.rs b/crates/fspy_preload_unix/src/client.rs index 734d3718d..9f250d8c3 100644 --- a/crates/fspy_preload_unix/src/client.rs +++ b/crates/fspy_preload_unix/src/client.rs @@ -1,43 +1,28 @@ -use std::{cell::Cell, sync::OnceLock}; +use std::sync::OnceLock; use convert::{ToAbsolutePath, ToAccessMode}; pub use fspy_client_unix::{Client, convert, raw_exec}; static CLIENT: OnceLock> = OnceLock::new(); -// Resolving and reporting a file access can call another interposed function. -// Suppress same-thread re-entry to prevent recursive access handling while -// still recording accesses from other threads. -thread_local! { - static HANDLING_OPEN: Cell = const { Cell::new(false) }; -} - -struct ResetHandling<'a>(&'a Cell); -impl Drop for ResetHandling<'_> { - fn drop(&mut self) { - self.0.set(false); - } -} - pub fn global_client() -> Option<&'static Client<'static>> { CLIENT.get() } +// The handler needs no re-entry guard: on Linux everything it calls is a +// raw syscall — nothing binds through the PLT, where LD_PRELOAD would +// resolve to our own interposers — and on macOS dyld never applies +// `__interpose` tuples to the interposing image's own bindings, the same +// exemption every `original()` forward relies on. Keep the handler free of +// bindable libc calls on Linux: a call that binds to an interposer here +// recurses until the traced process overflows its stack. pub unsafe fn handle_open(path: impl ToAbsolutePath, mode: impl ToAccessMode) { - HANDLING_OPEN.with(|handling| { - if handling.replace(true) { - return; - } - - let _reset = ResetHandling(handling); - - if let Some(client) = global_client() { - let allocator = fspy_nostd_alloc::pooled_bump(); - // SAFETY: path and mode contain valid pointers/values forwarded - // from the interposed function's caller. - unsafe { client.try_handle_open(path, mode, allocator) }.unwrap(); - } - }); + if let Some(client) = global_client() { + let allocator = fspy_nostd_alloc::pooled_bump(); + // SAFETY: path and mode contain valid pointers/values forwarded + // from the interposed function's caller. + unsafe { client.try_handle_open(path, mode, allocator) }.unwrap(); + } } #[cfg(not(test))]