From 7f6a8de9b7bb70c79335001a18ce87d5194ddbc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Barreteau?= Date: Sat, 26 Jul 2025 00:24:03 -0400 Subject: [PATCH] Fix syscall_info According to the man page: > The addr argument contains the size of the buffer pointed to by the > data argument (i.e., sizeof(struct ptrace_syscall_info)). We were setting the data argument, so this syscall was returning garbage. This is easily reproduced and verified by looking at the `op` value, which should range from 0 to 3. The PR associated with this commit contains a sample program to demonstrate this. The fix is done in the `ptrace_get_data` helper to avoid duplicating its implementation just for `syscall_info`. Of all the other callers, all but one are documented as ignoring `addr`. The other one (`PTRACE_GETREGS`) is documented as ignoring `addr` _except on SPARC systems_, on which `addr` and `data` are reversed. However, this is already not respected by `nix`, so this changes is not disruptive in this regard. There should be no performance concerns as we are replacing one constant with another. --- changelog/2653.fixed.md | 1 + examples/ptrace.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/sys/ptrace/linux.rs | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 changelog/2653.fixed.md create mode 100644 examples/ptrace.rs diff --git a/changelog/2653.fixed.md b/changelog/2653.fixed.md new file mode 100644 index 0000000000..fb2885671d --- /dev/null +++ b/changelog/2653.fixed.md @@ -0,0 +1 @@ +Fixed `nix::sys::ptrace::syscall_info`, which was not setting the `data` argument properly, causing garbage values to be returned. diff --git a/examples/ptrace.rs b/examples/ptrace.rs new file mode 100644 index 0000000000..32cb22f0b0 --- /dev/null +++ b/examples/ptrace.rs @@ -0,0 +1,39 @@ +//! Traces a child process using `ptrace`. +//! +//! The child issues a single `write` syscall, which is printed upon entry and exit. + +#[cfg(all(target_os = "linux", target_env = "gnu"))] +fn main() { + let pid = unsafe { nix::unistd::fork().unwrap() }; + + match pid { + nix::unistd::ForkResult::Child => { + nix::sys::ptrace::traceme().unwrap(); + nix::sys::signal::raise(nix::sys::signal::Signal::SIGCONT).unwrap(); + println!("I'm issuing a syscall!"); + } + nix::unistd::ForkResult::Parent { child } => { + nix::sys::wait::waitpid(Some(child), None).unwrap(); + nix::sys::ptrace::setoptions( + child, + nix::sys::ptrace::Options::PTRACE_O_TRACESYSGOOD, + ) + .unwrap(); + + nix::sys::ptrace::syscall(child, None).unwrap(); + nix::sys::wait::waitpid(Some(child), None).unwrap(); + let syscall_info = nix::sys::ptrace::syscall_info(child).unwrap(); + println!("{syscall_info:?}"); + assert!(syscall_info.op == libc::PTRACE_SYSCALL_INFO_ENTRY); + + nix::sys::ptrace::syscall(child, None).unwrap(); + nix::sys::wait::waitpid(Some(child), None).unwrap(); + let syscall_info = nix::sys::ptrace::syscall_info(child).unwrap(); + println!("{syscall_info:?}"); + assert!(syscall_info.op == libc::PTRACE_SYSCALL_INFO_EXIT); + } + } +} + +#[cfg(not(all(target_os = "linux", target_env = "gnu")))] +fn main() {} diff --git a/src/sys/ptrace/linux.rs b/src/sys/ptrace/linux.rs index 8c1a46ba8d..d067ef5789 100644 --- a/src/sys/ptrace/linux.rs +++ b/src/sys/ptrace/linux.rs @@ -504,7 +504,7 @@ fn ptrace_get_data(request: Request, pid: Pid) -> Result { libc::ptrace( request as RequestType, libc::pid_t::from(pid), - ptr::null_mut::(), + std::mem::size_of::(), data.as_mut_ptr(), ) };