• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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