Skip to content

Commit d676f77

Browse files
wan9chicodex
andcommitted
refactor(fspy): return arena-backed absolute paths
Co-authored-by: GPT-5 Codex <codex@openai.com>
1 parent 961414c commit d676f77

3 files changed

Lines changed: 53 additions & 39 deletions

File tree

crates/fspy_preload_unix/src/client/convert.rs

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ use std::{
66
path::PathBuf,
77
};
88

9-
use bstr::{BStr, ByteSlice};
9+
use allocator_api2::{alloc::Allocator, vec::Vec};
10+
use bstr::ByteSlice;
1011
use fspy_shared::ipc::AccessMode;
1112
use libc::{c_char, c_int};
1213
use nix::unistd::getcwd;
@@ -40,55 +41,75 @@ fn get_fd_path(fd: RawFd) -> nix::Result<Option<PathBuf>> {
4041
}
4142
}
4243

44+
pub struct AbsolutePath<A: Allocator>(Vec<u8, A>);
45+
46+
impl<A: Allocator> AbsolutePath<A> {
47+
fn new_in(path: &[u8], allocator: A) -> Self {
48+
let mut bytes = Vec::with_capacity_in(path.len() + 1, allocator);
49+
bytes.extend_from_slice(path);
50+
bytes.push(0);
51+
Self(bytes)
52+
}
53+
54+
pub fn as_bytes(&self) -> &[u8] {
55+
&self.0[..self.0.len() - 1]
56+
}
57+
58+
#[cfg(target_os = "linux")]
59+
pub fn as_ptr(&self) -> *const c_char {
60+
self.0.as_ptr().cast()
61+
}
62+
}
63+
4364
pub trait ToAbsolutePath {
44-
unsafe fn to_absolute_path<R, F: FnOnce(Option<&BStr>) -> nix::Result<R>>(
65+
unsafe fn to_absolute_path<A: Allocator>(
4566
self,
46-
f: F,
47-
) -> nix::Result<R>;
67+
allocator: A,
68+
) -> nix::Result<Option<AbsolutePath<A>>>;
4869
}
4970

5071
pub struct Fd(pub c_int);
5172
impl ToAbsolutePath for Fd {
52-
unsafe fn to_absolute_path<R, F: FnOnce(Option<&BStr>) -> nix::Result<R>>(
73+
unsafe fn to_absolute_path<A: Allocator>(
5374
self,
54-
f: F,
55-
) -> nix::Result<R> {
75+
allocator: A,
76+
) -> nix::Result<Option<AbsolutePath<A>>> {
5677
let path = get_fd_path(self.0)?;
57-
f(path.as_ref().map(|p| p.as_os_str().as_bytes().as_bstr()))
78+
Ok(path.map(|path| AbsolutePath::new_in(path.as_os_str().as_bytes(), allocator)))
5879
}
5980
}
6081

6182
pub struct PathAt(pub c_int, pub *const c_char);
6283

6384
impl ToAbsolutePath for PathAt {
64-
unsafe fn to_absolute_path<R, F: FnOnce(Option<&BStr>) -> nix::Result<R>>(
85+
unsafe fn to_absolute_path<A: Allocator>(
6586
self,
66-
f: F,
67-
) -> nix::Result<R> {
87+
allocator: A,
88+
) -> nix::Result<Option<AbsolutePath<A>>> {
6889
// SAFETY: self.1 is a non-null pointer to a valid null-terminated C string, as guaranteed by the libc calling convention
6990
let pathname = unsafe { CStr::from_ptr(self.1) }.to_bytes().as_bstr();
7091

7192
if pathname.first().copied() == Some(b'/') {
72-
f(pathname.into())
93+
Ok(Some(AbsolutePath::new_in(pathname, allocator)))
7394
} else {
7495
let Some(mut abs_path) = get_fd_path(self.0)? else {
75-
return f(None);
96+
return Ok(None);
7697
};
7798
if !pathname.is_empty() {
7899
abs_path.push(OsStr::from_bytes(pathname));
79100
}
80-
f(Some(abs_path.as_os_str().as_bytes().as_bstr()))
101+
Ok(Some(AbsolutePath::new_in(abs_path.as_os_str().as_bytes(), allocator)))
81102
}
82103
}
83104
}
84105

85106
impl ToAbsolutePath for *const c_char {
86-
unsafe fn to_absolute_path<R, F: FnOnce(Option<&BStr>) -> nix::Result<R>>(
107+
unsafe fn to_absolute_path<A: Allocator>(
87108
self,
88-
f: F,
89-
) -> nix::Result<R> {
109+
allocator: A,
110+
) -> nix::Result<Option<AbsolutePath<A>>> {
90111
// SAFETY: delegates to PathAt::to_absolute_path with AT_FDCWD and the caller-provided C string pointer
91-
unsafe { PathAt(libc::AT_FDCWD, self).to_absolute_path(f) }
112+
unsafe { PathAt(libc::AT_FDCWD, self).to_absolute_path(allocator) }
92113
}
93114
}
94115

crates/fspy_preload_unix/src/client/mod.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,15 +109,12 @@ impl Client {
109109
) -> anyhow::Result<()> {
110110
// SAFETY: mode contains a valid pointer (if ModeStr) or a plain value, as provided by the caller
111111
let mode = unsafe { mode.to_access_mode() };
112+
let arena = sigsafe_alloc::arena();
112113
// SAFETY: path contains valid pointers to C strings/file descriptors, as provided by the caller
113-
let () = unsafe {
114-
path.to_absolute_path(|abs_path| {
115-
let Some(abs_path) = abs_path else {
116-
return Ok(Ok(()));
117-
};
118-
Ok(self.send(mode, Path::new(OsStr::from_bytes(abs_path))))
119-
})
120-
}??;
114+
let Some(abs_path) = (unsafe { path.to_absolute_path(&arena) })? else {
115+
return Ok(());
116+
};
117+
self.send(mode, Path::new(OsStr::from_bytes(abs_path.as_bytes())))?;
121118

122119
Ok(())
123120
}

crates/fspy_preload_unix/src/interceptions/spawn/exec/mod.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
mod with_argv;
22

3-
#[cfg(target_os = "linux")]
4-
use std::ffi::CString;
5-
63
use fspy_shared_unix::exec::ExecResolveConfig;
74
use libc::{c_char, c_int};
85
use with_argv::with_argv;
@@ -195,27 +192,26 @@ mod linux_only {
195192
reason = "suppresses unused warning on *::original"
196193
)]
197194
let _unused = execveat::original;
195+
let arena = sigsafe_alloc::arena();
198196
// SAFETY: PathAt wraps a valid dirfd and pathname pointer from the interposed execveat call
199-
let abs_path_result = unsafe {
200-
PathAt(dirfd, pathname).to_absolute_path(|path| {
201-
let Some(path) = path else {
202-
return Ok(None);
203-
};
204-
Ok(Some(CString::new(&**path).unwrap()))
205-
})
206-
};
197+
let abs_path_result = unsafe { PathAt(dirfd, pathname).to_absolute_path(&arena) };
207198
let abs_path = match abs_path_result {
208199
Ok(None) => {
209200
// SAFETY: forwarding the original arguments to the real execveat syscall
210201
return unsafe { execveat::original()(dirfd, pathname, argv, envp, flags) };
211202
}
212-
Ok(Some(path)) => path.as_ptr(),
203+
Ok(Some(path)) => path,
213204
Err(errno) => {
214205
errno.set();
215206
return -1;
216207
}
217208
};
218-
handle_exec(ExecResolveConfig::search_path_disabled(), abs_path, argv.cast(), envp.cast())
209+
handle_exec(
210+
ExecResolveConfig::search_path_disabled(),
211+
abs_path.as_ptr(),
212+
argv.cast(),
213+
envp.cast(),
214+
)
219215
}
220216

221217
intercept!(fexecve(64): unsafe extern "C" fn(

0 commit comments

Comments
 (0)