1 #[cfg(all(
2 target_os = "linux",
3 target_env = "gnu",
4 any(target_arch = "x86_64", target_arch = "x86")
5 ))]
6 use memoffset::offset_of;
7 use nix::errno::Errno;
8 use nix::sys::ptrace;
9 #[cfg(linux_android)]
10 use nix::sys::ptrace::Options;
11 use nix::unistd::getpid;
12
13 #[cfg(linux_android)]
14 use std::mem;
15
16 use crate::*;
17
18 #[test]
test_ptrace()19 fn test_ptrace() {
20 // Just make sure ptrace can be called at all, for now.
21 // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
22 require_capability!("test_ptrace", CAP_SYS_PTRACE);
23 let err = ptrace::attach(getpid()).unwrap_err();
24 assert!(
25 err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS
26 );
27 }
28
29 // Just make sure ptrace_setoptions can be called at all, for now.
30 #[test]
31 #[cfg(linux_android)]
test_ptrace_setoptions()32 fn test_ptrace_setoptions() {
33 require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
34 let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
35 .unwrap_err();
36 assert_ne!(err, Errno::EOPNOTSUPP);
37 }
38
39 // Just make sure ptrace_getevent can be called at all, for now.
40 #[test]
41 #[cfg(linux_android)]
test_ptrace_getevent()42 fn test_ptrace_getevent() {
43 require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
44 let err = ptrace::getevent(getpid()).unwrap_err();
45 assert_ne!(err, Errno::EOPNOTSUPP);
46 }
47
48 // Just make sure ptrace_getsiginfo can be called at all, for now.
49 #[test]
50 #[cfg(linux_android)]
test_ptrace_getsiginfo()51 fn test_ptrace_getsiginfo() {
52 require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
53 if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
54 panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
55 }
56 }
57
58 // Just make sure ptrace_setsiginfo can be called at all, for now.
59 #[test]
60 #[cfg(linux_android)]
test_ptrace_setsiginfo()61 fn test_ptrace_setsiginfo() {
62 require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE);
63 let siginfo = unsafe { mem::zeroed() };
64 if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
65 panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
66 }
67 }
68
69 #[test]
test_ptrace_cont()70 fn test_ptrace_cont() {
71 use nix::sys::ptrace;
72 use nix::sys::signal::{raise, Signal};
73 use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
74 use nix::unistd::fork;
75 use nix::unistd::ForkResult::*;
76
77 require_capability!("test_ptrace_cont", CAP_SYS_PTRACE);
78
79 let _m = crate::FORK_MTX.lock();
80
81 // FIXME: qemu-user doesn't implement ptrace on all architectures
82 // and returns ENOSYS in this case.
83 // We (ab)use this behavior to detect the affected platforms
84 // and skip the test then.
85 // On valid platforms the ptrace call should return Errno::EPERM, this
86 // is already tested by `test_ptrace`.
87 let err = ptrace::attach(getpid()).unwrap_err();
88 if err == Errno::ENOSYS {
89 return;
90 }
91
92 match unsafe { fork() }.expect("Error: Fork Failed") {
93 Child => {
94 ptrace::traceme().unwrap();
95 // As recommended by ptrace(2), raise SIGTRAP to pause the child
96 // until the parent is ready to continue
97 loop {
98 raise(Signal::SIGTRAP).unwrap();
99 }
100 }
101 Parent { child } => {
102 assert_eq!(
103 waitpid(child, None),
104 Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
105 );
106 ptrace::cont(child, None).unwrap();
107 assert_eq!(
108 waitpid(child, None),
109 Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
110 );
111 ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
112 match waitpid(child, None) {
113 Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
114 if pid == child =>
115 {
116 // FIXME It's been observed on some systems (apple) the
117 // tracee may not be killed but remain as a zombie process
118 // affecting other wait based tests. Add an extra kill just
119 // to make sure there are no zombies.
120 let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
121 while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
122 let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
123 }
124 }
125 _ => panic!("The process should have been killed"),
126 }
127 }
128 }
129 }
130
131 #[cfg(target_os = "linux")]
132 #[test]
test_ptrace_interrupt()133 fn test_ptrace_interrupt() {
134 use nix::sys::ptrace;
135 use nix::sys::signal::Signal;
136 use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
137 use nix::unistd::fork;
138 use nix::unistd::ForkResult::*;
139 use std::thread::sleep;
140 use std::time::Duration;
141
142 require_capability!("test_ptrace_interrupt", CAP_SYS_PTRACE);
143
144 let _m = crate::FORK_MTX.lock();
145
146 match unsafe { fork() }.expect("Error: Fork Failed") {
147 Child => loop {
148 sleep(Duration::from_millis(1000));
149 },
150 Parent { child } => {
151 ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
152 .unwrap();
153 ptrace::interrupt(child).unwrap();
154 assert_eq!(
155 waitpid(child, None),
156 Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))
157 );
158 ptrace::syscall(child, None).unwrap();
159 assert_eq!(
160 waitpid(child, None),
161 Ok(WaitStatus::PtraceSyscall(child))
162 );
163 ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
164 match waitpid(child, None) {
165 Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
166 if pid == child =>
167 {
168 let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
169 while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
170 let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
171 }
172 }
173 _ => panic!("The process should have been killed"),
174 }
175 }
176 }
177 }
178
179 // ptrace::{setoptions, getregs} are only available in these platforms
180 #[cfg(all(
181 target_os = "linux",
182 target_env = "gnu",
183 any(
184 target_arch = "x86_64",
185 target_arch = "x86",
186 target_arch = "aarch64",
187 target_arch = "riscv64",
188 )
189 ))]
190 #[test]
test_ptrace_syscall()191 fn test_ptrace_syscall() {
192 use nix::sys::ptrace;
193 use nix::sys::signal::kill;
194 use nix::sys::signal::Signal;
195 use nix::sys::wait::{waitpid, WaitStatus};
196 use nix::unistd::fork;
197 use nix::unistd::getpid;
198 use nix::unistd::ForkResult::*;
199
200 require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE);
201
202 let _m = crate::FORK_MTX.lock();
203
204 match unsafe { fork() }.expect("Error: Fork Failed") {
205 Child => {
206 ptrace::traceme().unwrap();
207 // first sigstop until parent is ready to continue
208 let pid = getpid();
209 kill(pid, Signal::SIGSTOP).unwrap();
210 kill(pid, Signal::SIGTERM).unwrap();
211 unsafe {
212 ::libc::_exit(0);
213 }
214 }
215
216 Parent { child } => {
217 assert_eq!(
218 waitpid(child, None),
219 Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))
220 );
221
222 // set this option to recognize syscall-stops
223 ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
224 .unwrap();
225
226 #[cfg(target_arch = "x86_64")]
227 let get_syscall_id =
228 || ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
229
230 #[cfg(target_arch = "x86")]
231 let get_syscall_id =
232 || ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
233
234 #[cfg(target_arch = "aarch64")]
235 let get_syscall_id =
236 || ptrace::getregs(child).unwrap().regs[8] as libc::c_long;
237
238 #[cfg(target_arch = "riscv64")]
239 let get_syscall_id =
240 || ptrace::getregs(child).unwrap().a7 as libc::c_long;
241
242 // this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
243 #[cfg(target_arch = "x86_64")]
244 let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
245 #[cfg(target_arch = "x86")]
246 let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
247
248 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
249 let get_syscall_from_user_area = || {
250 // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
251 let rax_offset = offset_of!(libc::user, regs) + rax_offset;
252 ptrace::read_user(child, rax_offset as _).unwrap()
253 as libc::c_long
254 };
255
256 // kill entry
257 ptrace::syscall(child, None).unwrap();
258 assert_eq!(
259 waitpid(child, None),
260 Ok(WaitStatus::PtraceSyscall(child))
261 );
262 assert_eq!(get_syscall_id(), ::libc::SYS_kill);
263 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
264 assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
265
266 // kill exit
267 ptrace::syscall(child, None).unwrap();
268 assert_eq!(
269 waitpid(child, None),
270 Ok(WaitStatus::PtraceSyscall(child))
271 );
272 assert_eq!(get_syscall_id(), ::libc::SYS_kill);
273 #[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
274 assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
275
276 // receive signal
277 ptrace::syscall(child, None).unwrap();
278 assert_eq!(
279 waitpid(child, None),
280 Ok(WaitStatus::Stopped(child, Signal::SIGTERM))
281 );
282
283 // inject signal
284 ptrace::syscall(child, Signal::SIGTERM).unwrap();
285 assert_eq!(
286 waitpid(child, None),
287 Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))
288 );
289 }
290 }
291 }
292
293 #[cfg(all(
294 target_os = "linux",
295 target_env = "gnu",
296 any(
297 target_arch = "x86_64",
298 target_arch = "x86",
299 target_arch = "aarch64",
300 target_arch = "riscv64",
301 )
302 ))]
303 #[test]
test_ptrace_regsets()304 fn test_ptrace_regsets() {
305 use nix::sys::ptrace::{self, getregset, regset, setregset};
306 use nix::sys::signal::*;
307 use nix::sys::wait::{waitpid, WaitStatus};
308 use nix::unistd::fork;
309 use nix::unistd::ForkResult::*;
310
311 require_capability!("test_ptrace_regsets", CAP_SYS_PTRACE);
312
313 let _m = crate::FORK_MTX.lock();
314
315 match unsafe { fork() }.expect("Error: Fork Failed") {
316 Child => {
317 ptrace::traceme().unwrap();
318 // As recommended by ptrace(2), raise SIGTRAP to pause the child
319 // until the parent is ready to continue
320 loop {
321 raise(Signal::SIGTRAP).unwrap();
322 }
323 }
324
325 Parent { child } => {
326 assert_eq!(
327 waitpid(child, None),
328 Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
329 );
330 let mut regstruct =
331 getregset::<regset::NT_PRSTATUS>(child).unwrap();
332 let mut fpregstruct =
333 getregset::<regset::NT_PRFPREG>(child).unwrap();
334
335 #[cfg(target_arch = "x86_64")]
336 let (reg, fpreg) =
337 (&mut regstruct.r15, &mut fpregstruct.st_space[5]);
338 #[cfg(target_arch = "x86")]
339 let (reg, fpreg) =
340 (&mut regstruct.edx, &mut fpregstruct.st_space[5]);
341 #[cfg(target_arch = "aarch64")]
342 let (reg, fpreg) =
343 (&mut regstruct.regs[16], &mut fpregstruct.vregs[5]);
344 #[cfg(target_arch = "riscv64")]
345 let (reg, fpreg) = (&mut regstruct.t1, &mut fpregstruct.__f[5]);
346
347 *reg = 0xdeadbeefu32 as _;
348 *fpreg = 0xfeedfaceu32 as _;
349 let _ = setregset::<regset::NT_PRSTATUS>(child, regstruct);
350 regstruct = getregset::<regset::NT_PRSTATUS>(child).unwrap();
351 let _ = setregset::<regset::NT_PRFPREG>(child, fpregstruct);
352 fpregstruct = getregset::<regset::NT_PRFPREG>(child).unwrap();
353
354 #[cfg(target_arch = "x86_64")]
355 let (reg, fpreg) = (regstruct.r15, fpregstruct.st_space[5]);
356 #[cfg(target_arch = "x86")]
357 let (reg, fpreg) = (regstruct.edx, fpregstruct.st_space[5]);
358 #[cfg(target_arch = "aarch64")]
359 let (reg, fpreg) = (regstruct.regs[16], fpregstruct.vregs[5]);
360 #[cfg(target_arch = "riscv64")]
361 let (reg, fpreg) = (regstruct.t1, fpregstruct.__f[5]);
362 assert_eq!(reg, 0xdeadbeefu32 as _);
363 assert_eq!(fpreg, 0xfeedfaceu32 as _);
364
365 ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
366 match waitpid(child, None) {
367 Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
368 if pid == child => {}
369 _ => panic!("The process should have been killed"),
370 }
371 }
372 }
373 }
374