diff options
author | Geoffrey Thomas <geofft@ldpreload.com> | 2017-03-28 16:55:36 -0400 |
---|---|---|
committer | Marcin Mielniczuk <marmistrz.dev@zoho.eu> | 2017-07-25 09:09:52 +0200 |
commit | 903a52f602054dff1258b5ca04591f0c5edbe08b (patch) | |
tree | 4768078a792799c5a2b737ecd3d65da0da9dcf54 /test/sys | |
parent | 845453b3d2be1a760782b0c7eca02a1d9360f947 (diff) | |
download | nix-903a52f602054dff1258b5ca04591f0c5edbe08b.zip |
Add WaitStatus::PtraceSyscall for use with PTRACE_O_TRACESYSGOOD
The recommended way to trace syscalls with ptrace is to set the
PTRACE_O_TRACESYSGOOD option, to distinguish syscall stops from
receiving an actual SIGTRAP. In C, this would cause WSTOPSIG to return
SIGTRAP | 0x80, but nix wants to parse that as an actual signal.
Add another wait status type for syscall stops (in the language of the
ptrace(2) manpage, "PTRACE_EVENT stops" and "Syscall-stops" are
different things), and mask out bit 0x80 from signals before trying to
parse it.
Closes #550
Diffstat (limited to 'test/sys')
-rw-r--r-- | test/sys/test_wait.rs | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/test/sys/test_wait.rs b/test/sys/test_wait.rs index 2e28d9e7..5f6c9231 100644 --- a/test/sys/test_wait.rs +++ b/test/sys/test_wait.rs @@ -34,3 +34,53 @@ fn test_wait_exit() { Err(_) => panic!("Error: Fork Failed") } } + +#[cfg(any(target_os = "linux", target_os = "android"))] +// FIXME: qemu-user doesn't implement ptrace on most arches +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +mod ptrace { + use nix::sys::ptrace::*; + use nix::sys::ptrace::ptrace::*; + use nix::sys::signal::*; + use nix::sys::wait::*; + use nix::unistd::*; + use nix::unistd::ForkResult::*; + use std::{ptr, process}; + + fn ptrace_child() -> ! { + let _ = ptrace(PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut()); + // As recommended by ptrace(2), raise SIGTRAP to pause the child + // until the parent is ready to continue + let _ = raise(SIGTRAP); + process::exit(0) + } + + fn ptrace_parent(child: Pid) { + // Wait for the raised SIGTRAP + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, SIGTRAP))); + // We want to test a syscall stop and a PTRACE_EVENT stop + assert!(ptrace_setoptions(child, PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXIT).is_ok()); + + // First, stop on the next system call, which will be exit() + assert!(ptrace(PTRACE_SYSCALL, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + // Then get the ptrace event for the process exiting + assert!(ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, SIGTRAP, PTRACE_EVENT_EXIT))); + // Finally get the normal wait() result, now that the process has exited + assert!(ptrace(PTRACE_CONT, child, ptr::null_mut(), ptr::null_mut()).is_ok()); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0))); + } + + #[test] + fn test_wait_ptrace() { + #[allow(unused_variables)] + let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + match fork() { + Ok(Child) => ptrace_child(), + Ok(Parent { child }) => ptrace_parent(child), + Err(_) => panic!("Error: Fork Failed") + } + } +} |