From c1a07b92997b1c2e092ea6c72e84f3b3c671f59e Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Mon, 17 Jun 2019 17:35:38 -0300 Subject: [PATCH] Add ptrace::syscall test --- test/sys/test_ptrace.rs | 73 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 24d9b522ee..1b7f0f3d42 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -105,3 +105,76 @@ fn test_ptrace_cont() { }, } } + +// ptrace::setoptions needed to trace syscalls is only available in these platforms +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_ptrace_syscall() { + use nix::sys::signal::kill; + use nix::sys::ptrace; + use nix::sys::signal::Signal; + use nix::sys::wait::{waitpid, WaitStatus}; + use nix::unistd::fork; + use nix::unistd::getpid; + use nix::unistd::ForkResult::*; + + let _m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test"); + + // FIXME: qemu-user doesn't implement ptrace on all architectures + // and retunrs ENOSYS in this case. + // We (ab)use this behavior to detect the affected platforms + // and skip the test then. + // On valid platforms the ptrace call should return Errno::EPERM, this + // is already tested by `test_ptrace`. + let err = ptrace::attach(getpid()).unwrap_err(); + if err == Error::Sys(Errno::ENOSYS) { + return; + } + + match fork().expect("Error: Fork Failed") { + Child => { + ptrace::traceme().unwrap(); + // first sigstop until parent is ready to continue + kill(getpid(), Signal::SIGSTOP).unwrap(); + kill(getpid(), Signal::SIGTERM).unwrap(); + }, + + Parent { child } => { + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))); + + // set this option to recognize syscall-stops + ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap(); + + let get_syscall_id = || ptrace::getregs(child).unwrap().orig_rax as i64; + + + // getpid entry + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!(get_syscall_id(), libc::SYS_getpid); + + // getpid exit + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!(get_syscall_id(), libc::SYS_getpid); + + // kill entry + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!(get_syscall_id(), libc::SYS_kill); + + // kill exit + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!(get_syscall_id(), libc::SYS_kill); + + // receive signal + ptrace::syscall(child, None).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTERM))); + + // inject signal + ptrace::syscall(child, Signal::SIGTERM).unwrap(); + assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))); + }, + } +}