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