1 use nix::Error;
2 use nix::errno::Errno;
3 use nix::unistd::getpid;
4 use nix::sys::ptrace;
5 #[cfg(any(target_os = "android", target_os = "linux"))]
6 use nix::sys::ptrace::Options;
7
8 #[cfg(any(target_os = "android", target_os = "linux"))]
9 use std::mem;
10
11 use crate::*;
12
13 #[test]
test_ptrace()14 fn test_ptrace() {
15 // Just make sure ptrace can be called at all, for now.
16 // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
17 require_capability!(CAP_SYS_PTRACE);
18 let err = ptrace::attach(getpid()).unwrap_err();
19 assert!(err == Error::Sys(Errno::EPERM) || err == Error::Sys(Errno::EINVAL) ||
20 err == Error::Sys(Errno::ENOSYS));
21 }
22
23 // Just make sure ptrace_setoptions can be called at all, for now.
24 #[test]
25 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptrace_setoptions()26 fn test_ptrace_setoptions() {
27 require_capability!(CAP_SYS_PTRACE);
28 let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD).unwrap_err();
29 assert!(err != Error::UnsupportedOperation);
30 }
31
32 // Just make sure ptrace_getevent can be called at all, for now.
33 #[test]
34 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptrace_getevent()35 fn test_ptrace_getevent() {
36 require_capability!(CAP_SYS_PTRACE);
37 let err = ptrace::getevent(getpid()).unwrap_err();
38 assert!(err != Error::UnsupportedOperation);
39 }
40
41 // Just make sure ptrace_getsiginfo can be called at all, for now.
42 #[test]
43 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptrace_getsiginfo()44 fn test_ptrace_getsiginfo() {
45 require_capability!(CAP_SYS_PTRACE);
46 if let Err(Error::UnsupportedOperation) = ptrace::getsiginfo(getpid()) {
47 panic!("ptrace_getsiginfo returns Error::UnsupportedOperation!");
48 }
49 }
50
51 // Just make sure ptrace_setsiginfo can be called at all, for now.
52 #[test]
53 #[cfg(any(target_os = "android", target_os = "linux"))]
test_ptrace_setsiginfo()54 fn test_ptrace_setsiginfo() {
55 require_capability!(CAP_SYS_PTRACE);
56 let siginfo = unsafe { mem::zeroed() };
57 if let Err(Error::UnsupportedOperation) = ptrace::setsiginfo(getpid(), &siginfo) {
58 panic!("ptrace_setsiginfo returns Error::UnsupportedOperation!");
59 }
60 }
61
62
63 #[test]
test_ptrace_cont()64 fn test_ptrace_cont() {
65 use nix::sys::ptrace;
66 use nix::sys::signal::{raise, Signal};
67 use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
68 use nix::unistd::fork;
69 use nix::unistd::ForkResult::*;
70
71 require_capability!(CAP_SYS_PTRACE);
72
73 let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
74
75 // FIXME: qemu-user doesn't implement ptrace on all architectures
76 // and retunrs ENOSYS in this case.
77 // We (ab)use this behavior to detect the affected platforms
78 // and skip the test then.
79 // On valid platforms the ptrace call should return Errno::EPERM, this
80 // is already tested by `test_ptrace`.
81 let err = ptrace::attach(getpid()).unwrap_err();
82 if err == Error::Sys(Errno::ENOSYS) {
83 return;
84 }
85
86 match unsafe{fork()}.expect("Error: Fork Failed") {
87 Child => {
88 ptrace::traceme().unwrap();
89 // As recommended by ptrace(2), raise SIGTRAP to pause the child
90 // until the parent is ready to continue
91 loop {
92 raise(Signal::SIGTRAP).unwrap();
93 }
94
95 },
96 Parent { child } => {
97 assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
98 ptrace::cont(child, None).unwrap();
99 assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTRAP)));
100 ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
101 match waitpid(child, None) {
102 Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _)) if pid == child => {
103 // FIXME It's been observed on some systems (apple) the
104 // tracee may not be killed but remain as a zombie process
105 // affecting other wait based tests. Add an extra kill just
106 // to make sure there are no zombies.
107 let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
108 while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
109 let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
110 }
111 }
112 _ => panic!("The process should have been killed"),
113 }
114 },
115 }
116 }
117
118 // ptrace::{setoptions, getregs} are only available in these platforms
119 #[cfg(all(target_os = "linux",
120 any(target_arch = "x86_64",
121 target_arch = "x86"),
122 target_env = "gnu"))]
123 #[test]
test_ptrace_syscall()124 fn test_ptrace_syscall() {
125 use nix::sys::signal::kill;
126 use nix::sys::ptrace;
127 use nix::sys::signal::Signal;
128 use nix::sys::wait::{waitpid, WaitStatus};
129 use nix::unistd::fork;
130 use nix::unistd::getpid;
131 use nix::unistd::ForkResult::*;
132
133 require_capability!(CAP_SYS_PTRACE);
134
135 let _m = crate::FORK_MTX.lock().expect("Mutex got poisoned by another test");
136
137 match unsafe{fork()}.expect("Error: Fork Failed") {
138 Child => {
139 ptrace::traceme().unwrap();
140 // first sigstop until parent is ready to continue
141 let pid = getpid();
142 kill(pid, Signal::SIGSTOP).unwrap();
143 kill(pid, Signal::SIGTERM).unwrap();
144 unsafe { ::libc::_exit(0); }
145 },
146
147 Parent { child } => {
148 assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGSTOP)));
149
150 // set this option to recognize syscall-stops
151 ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD).unwrap();
152
153 #[cfg(target_arch = "x86_64")]
154 let get_syscall_id = || ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
155
156 #[cfg(target_arch = "x86")]
157 let get_syscall_id = || ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
158
159 // kill entry
160 ptrace::syscall(child, None).unwrap();
161 assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
162 assert_eq!(get_syscall_id(), ::libc::SYS_kill);
163
164 // kill exit
165 ptrace::syscall(child, None).unwrap();
166 assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
167 assert_eq!(get_syscall_id(), ::libc::SYS_kill);
168
169 // receive signal
170 ptrace::syscall(child, None).unwrap();
171 assert_eq!(waitpid(child, None), Ok(WaitStatus::Stopped(child, Signal::SIGTERM)));
172
173 // inject signal
174 ptrace::syscall(child, Signal::SIGTERM).unwrap();
175 assert_eq!(waitpid(child, None), Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false)));
176 },
177 }
178 }
179