diff options
Diffstat (limited to 'test/sys/test_ptrace.rs')
-rw-r--r-- | test/sys/test_ptrace.rs | 127 |
1 files changed, 82 insertions, 45 deletions
diff --git a/test/sys/test_ptrace.rs b/test/sys/test_ptrace.rs index 8556a548..e514832b 100644 --- a/test/sys/test_ptrace.rs +++ b/test/sys/test_ptrace.rs @@ -1,13 +1,14 @@ -#[cfg(all(target_os = "linux", - any(target_arch = "x86_64", - target_arch = "x86"), - target_env = "gnu"))] +#[cfg(all( + target_os = "linux", + any(target_arch = "x86_64", target_arch = "x86"), + target_env = "gnu" +))] use memoffset::offset_of; use nix::errno::Errno; -use nix::unistd::getpid; use nix::sys::ptrace; #[cfg(any(target_os = "android", target_os = "linux"))] use nix::sys::ptrace::Options; +use nix::unistd::getpid; #[cfg(any(target_os = "android", target_os = "linux"))] use std::mem; @@ -20,8 +21,9 @@ fn test_ptrace() { // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS require_capability!("test_ptrace", CAP_SYS_PTRACE); let err = ptrace::attach(getpid()).unwrap_err(); - assert!(err == Errno::EPERM || err == Errno::EINVAL || - err == Errno::ENOSYS); + assert!( + err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS + ); } // Just make sure ptrace_setoptions can be called at all, for now. @@ -29,7 +31,8 @@ fn test_ptrace() { #[cfg(any(target_os = "android", target_os = "linux"))] fn test_ptrace_setoptions() { require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE); - let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err(); + let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD) + .unwrap_err(); assert!(err != Errno::EOPNOTSUPP); } @@ -63,7 +66,6 @@ fn test_ptrace_setsiginfo() { } } - #[test] fn test_ptrace_cont() { use nix::sys::ptrace; @@ -87,7 +89,7 @@ fn test_ptrace_cont() { return; } - match unsafe{fork()}.expect("Error: Fork Failed") { + match unsafe { fork() }.expect("Error: Fork Failed") { Child => { ptrace::traceme().unwrap(); // As recommended by ptrace(2), raise SIGTRAP to pause the child @@ -95,15 +97,22 @@ fn test_ptrace_cont() { loop { raise(Signal::SIGTRAP).unwrap(); } - - }, + } Parent { child } => { - assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))); + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)) + ); ptrace::cont(child, None).unwrap(); - assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))); + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)) + ); ptrace::cont(child, Some(Signal::SIGKILL)).unwrap(); match waitpid(child, None) { - Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => { + Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) + if pid == child => + { // FIXME It's been observed on some systems (apple) the // tracee may not be killed but remain as a zombie process // affecting other wait based tests. Add an extra kill just @@ -115,7 +124,7 @@ fn test_ptrace_cont() { } _ => panic!("The process should have been killed"), } - }, + } } } @@ -134,22 +143,28 @@ fn test_ptrace_interrupt() { let _m = crate::FORK_MTX.lock(); - match unsafe{fork()}.expect("Error: Fork Failed") { - Child => { - loop { - sleep(Duration::from_millis(1000)); - } - + match unsafe { fork() }.expect("Error: Fork Failed") { + Child => loop { + sleep(Duration::from_millis(1000)); }, Parent { child } => { - ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap(); + ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD) + .unwrap(); ptrace::interrupt(child).unwrap(); - assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))); + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128)) + ); ptrace::syscall(child, None).unwrap(); - assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::PtraceSyscall(child)) + ); ptrace::detach(child, Some(Signal::SIGKILL)).unwrap(); match waitpid(child, None) { - Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => { + Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) + if pid == child => + { let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() { let _ = waitpid(child, Some(WaitPidFlag::WNOHANG)); @@ -157,19 +172,20 @@ fn test_ptrace_interrupt() { } _ => panic!("The process should have been killed"), } - }, + } } } // ptrace::{setoptions, getregs} are only available in these platforms -#[cfg(all(target_os = "linux", - any(target_arch = "x86_64", - target_arch = "x86"), - target_env = "gnu"))] +#[cfg(all( + target_os = "linux", + any(target_arch = "x86_64", target_arch = "x86"), + target_env = "gnu" +))] #[test] fn test_ptrace_syscall() { - use nix::sys::signal::kill; use nix::sys::ptrace; + use nix::sys::signal::kill; use nix::sys::signal::Signal; use nix::sys::wait::{waitpid, WaitStatus}; use nix::unistd::fork; @@ -180,27 +196,35 @@ fn test_ptrace_syscall() { let _m = crate::FORK_MTX.lock(); - match unsafe{fork()}.expect("Error: Fork Failed") { + match unsafe { fork() }.expect("Error: Fork Failed") { Child => { ptrace::traceme().unwrap(); // first sigstop until parent is ready to continue let pid = getpid(); kill(pid, Signal::SIGSTOP).unwrap(); kill(pid, Signal::SIGTERM).unwrap(); - unsafe { ::libc::_exit(0); } - }, + unsafe { + ::libc::_exit(0); + } + } Parent { child } => { - assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))); + 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(); + ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD) + .unwrap(); #[cfg(target_arch = "x86_64")] - let get_syscall_id = || ptrace::getregs(child).unwrap().orig_rax as libc::c_long; + let get_syscall_id = + || ptrace::getregs(child).unwrap().orig_rax as libc::c_long; #[cfg(target_arch = "x86")] - let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as libc::c_long; + let get_syscall_id = + || ptrace::getregs(child).unwrap().orig_eax as libc::c_long; // this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`. #[cfg(target_arch = "x86_64")] @@ -211,28 +235,41 @@ fn test_ptrace_syscall() { let get_syscall_from_user_area = || { // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86) let rax_offset = offset_of!(libc::user, regs) + rax_offset; - ptrace::read_user(child, rax_offset as _).unwrap() as libc::c_long + ptrace::read_user(child, rax_offset as _).unwrap() + as libc::c_long }; // kill entry ptrace::syscall(child, None).unwrap(); - assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::PtraceSyscall(child)) + ); assert_eq!(get_syscall_id(), ::libc::SYS_kill); assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill); // kill exit ptrace::syscall(child, None).unwrap(); - assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child))); + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::PtraceSyscall(child)) + ); assert_eq!(get_syscall_id(), ::libc::SYS_kill); assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill); // receive signal ptrace::syscall(child, None).unwrap(); - assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTERM))); + 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))); - }, + assert_eq!( + waitpid(child, None), + Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false)) + ); + } } } |