1 //! For detailed description of the ptrace requests, consult `man ptrace`.
2
3 use cfg_if::cfg_if;
4 use std::{mem, ptr};
5 use crate::Result;
6 use crate::errno::Errno;
7 use libc::{self, c_void, c_long, siginfo_t};
8 use crate::unistd::Pid;
9 use crate::sys::signal::Signal;
10
11 pub type AddressType = *mut ::libc::c_void;
12
13 #[cfg(all(
14 target_os = "linux",
15 any(all(target_arch = "x86_64",
16 any(target_env = "gnu", target_env = "musl")),
17 all(target_arch = "x86", target_env = "gnu"))
18 ))]
19 use libc::user_regs_struct;
20
21 cfg_if! {
22 if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
23 all(target_os = "linux", target_env = "gnu")))] {
24 #[doc(hidden)]
25 pub type RequestType = ::libc::c_uint;
26 } else {
27 #[doc(hidden)]
28 pub type RequestType = ::libc::c_int;
29 }
30 }
31
32 libc_enum!{
33 #[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))]
34 #[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))]
35 /// Ptrace Request enum defining the action to be taken.
36 #[non_exhaustive]
37 pub enum Request {
38 PTRACE_TRACEME,
39 PTRACE_PEEKTEXT,
40 PTRACE_PEEKDATA,
41 PTRACE_PEEKUSER,
42 PTRACE_POKETEXT,
43 PTRACE_POKEDATA,
44 PTRACE_POKEUSER,
45 PTRACE_CONT,
46 PTRACE_KILL,
47 PTRACE_SINGLESTEP,
48 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
49 all(target_os = "linux", any(target_env = "musl",
50 target_arch = "mips",
51 target_arch = "mips64",
52 target_arch = "x86_64",
53 target_pointer_width = "32"))))]
54 PTRACE_GETREGS,
55 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
56 all(target_os = "linux", any(target_env = "musl",
57 target_arch = "mips",
58 target_arch = "mips64",
59 target_arch = "x86_64",
60 target_pointer_width = "32"))))]
61 PTRACE_SETREGS,
62 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
63 all(target_os = "linux", any(target_env = "musl",
64 target_arch = "mips",
65 target_arch = "mips64",
66 target_arch = "x86_64",
67 target_pointer_width = "32"))))]
68 PTRACE_GETFPREGS,
69 #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
70 all(target_os = "linux", any(target_env = "musl",
71 target_arch = "mips",
72 target_arch = "mips64",
73 target_arch = "x86_64",
74 target_pointer_width = "32"))))]
75 PTRACE_SETFPREGS,
76 PTRACE_ATTACH,
77 PTRACE_DETACH,
78 #[cfg(all(target_os = "linux", any(target_env = "musl",
79 target_arch = "mips",
80 target_arch = "mips64",
81 target_arch = "x86",
82 target_arch = "x86_64")))]
83 PTRACE_GETFPXREGS,
84 #[cfg(all(target_os = "linux", any(target_env = "musl",
85 target_arch = "mips",
86 target_arch = "mips64",
87 target_arch = "x86",
88 target_arch = "x86_64")))]
89 PTRACE_SETFPXREGS,
90 PTRACE_SYSCALL,
91 PTRACE_SETOPTIONS,
92 PTRACE_GETEVENTMSG,
93 PTRACE_GETSIGINFO,
94 PTRACE_SETSIGINFO,
95 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
96 target_arch = "mips64"))))]
97 PTRACE_GETREGSET,
98 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
99 target_arch = "mips64"))))]
100 PTRACE_SETREGSET,
101 #[cfg(target_os = "linux")]
102 PTRACE_SEIZE,
103 #[cfg(target_os = "linux")]
104 PTRACE_INTERRUPT,
105 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
106 target_arch = "mips64"))))]
107 PTRACE_LISTEN,
108 #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
109 target_arch = "mips64"))))]
110 PTRACE_PEEKSIGINFO,
111 #[cfg(all(target_os = "linux", target_env = "gnu",
112 any(target_arch = "x86", target_arch = "x86_64")))]
113 PTRACE_SYSEMU,
114 #[cfg(all(target_os = "linux", target_env = "gnu",
115 any(target_arch = "x86", target_arch = "x86_64")))]
116 PTRACE_SYSEMU_SINGLESTEP,
117 }
118 }
119
120 libc_enum!{
121 #[repr(i32)]
122 /// Using the ptrace options the tracer can configure the tracee to stop
123 /// at certain events. This enum is used to define those events as defined
124 /// in `man ptrace`.
125 #[non_exhaustive]
126 pub enum Event {
127 /// Event that stops before a return from fork or clone.
128 PTRACE_EVENT_FORK,
129 /// Event that stops before a return from vfork or clone.
130 PTRACE_EVENT_VFORK,
131 /// Event that stops before a return from clone.
132 PTRACE_EVENT_CLONE,
133 /// Event that stops before a return from execve.
134 PTRACE_EVENT_EXEC,
135 /// Event for a return from vfork.
136 PTRACE_EVENT_VFORK_DONE,
137 /// Event for a stop before an exit. Unlike the waitpid Exit status program.
138 /// registers can still be examined
139 PTRACE_EVENT_EXIT,
140 /// Stop triggered by a seccomp rule on a tracee.
141 PTRACE_EVENT_SECCOMP,
142 /// Stop triggered by the `INTERRUPT` syscall, or a group stop,
143 /// or when a new child is attached.
144 PTRACE_EVENT_STOP,
145 }
146 }
147
148 libc_bitflags! {
149 /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
150 /// See `man ptrace` for more details.
151 pub struct Options: libc::c_int {
152 /// When delivering system call traps set a bit to allow tracer to
153 /// distinguish between normal stops or syscall stops. May not work on
154 /// all systems.
155 PTRACE_O_TRACESYSGOOD;
156 /// Stop tracee at next fork and start tracing the forked process.
157 PTRACE_O_TRACEFORK;
158 /// Stop tracee at next vfork call and trace the vforked process.
159 PTRACE_O_TRACEVFORK;
160 /// Stop tracee at next clone call and trace the cloned process.
161 PTRACE_O_TRACECLONE;
162 /// Stop tracee at next execve call.
163 PTRACE_O_TRACEEXEC;
164 /// Stop tracee at vfork completion.
165 PTRACE_O_TRACEVFORKDONE;
166 /// Stop tracee at next exit call. Stops before exit commences allowing
167 /// tracer to see location of exit and register states.
168 PTRACE_O_TRACEEXIT;
169 /// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more
170 /// details.
171 PTRACE_O_TRACESECCOMP;
172 /// Send a SIGKILL to the tracee if the tracer exits. This is useful
173 /// for ptrace jailers to prevent tracees from escaping their control.
174 #[cfg(any(target_os = "android", target_os = "linux"))]
175 PTRACE_O_EXITKILL;
176 }
177 }
178
ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long>179 fn ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> {
180 let ret = unsafe {
181 Errno::clear();
182 libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)
183 };
184 match Errno::result(ret) {
185 Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
186 err @ Err(..) => err,
187 }
188 }
189
190 /// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
191 #[cfg(all(
192 target_os = "linux",
193 any(all(target_arch = "x86_64",
194 any(target_env = "gnu", target_env = "musl")),
195 all(target_arch = "x86", target_env = "gnu"))
196 ))]
getregs(pid: Pid) -> Result<user_regs_struct>197 pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
198 ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
199 }
200
201 /// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
202 #[cfg(all(
203 target_os = "linux",
204 any(all(target_arch = "x86_64",
205 any(target_env = "gnu", target_env = "musl")),
206 all(target_arch = "x86", target_env = "gnu"))
207 ))]
setregs(pid: Pid, regs: user_regs_struct) -> Result<()>208 pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
209 let res = unsafe {
210 libc::ptrace(Request::PTRACE_SETREGS as RequestType,
211 libc::pid_t::from(pid),
212 ptr::null_mut::<c_void>(),
213 ®s as *const _ as *const c_void)
214 };
215 Errno::result(res).map(drop)
216 }
217
218 /// Function for ptrace requests that return values from the data field.
219 /// Some ptrace get requests populate structs or larger elements than `c_long`
220 /// and therefore use the data field to return values. This function handles these
221 /// requests.
ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T>222 fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
223 let mut data = mem::MaybeUninit::uninit();
224 let res = unsafe {
225 libc::ptrace(request as RequestType,
226 libc::pid_t::from(pid),
227 ptr::null_mut::<T>(),
228 data.as_mut_ptr() as *const _ as *const c_void)
229 };
230 Errno::result(res)?;
231 Ok(unsafe{ data.assume_init() })
232 }
233
ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long>234 unsafe fn ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> {
235 Errno::result(libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)).map(|_| 0)
236 }
237
238 /// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
setoptions(pid: Pid, options: Options) -> Result<()>239 pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
240 let res = unsafe {
241 libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType,
242 libc::pid_t::from(pid),
243 ptr::null_mut::<c_void>(),
244 options.bits() as *mut c_void)
245 };
246 Errno::result(res).map(drop)
247 }
248
249 /// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
getevent(pid: Pid) -> Result<c_long>250 pub fn getevent(pid: Pid) -> Result<c_long> {
251 ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
252 }
253
254 /// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
getsiginfo(pid: Pid) -> Result<siginfo_t>255 pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
256 ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
257 }
258
259 /// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()>260 pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
261 let ret = unsafe{
262 Errno::clear();
263 libc::ptrace(Request::PTRACE_SETSIGINFO as RequestType,
264 libc::pid_t::from(pid),
265 ptr::null_mut::<c_void>(),
266 sig as *const _ as *const c_void)
267 };
268 match Errno::result(ret) {
269 Ok(_) => Ok(()),
270 Err(e) => Err(e),
271 }
272 }
273
274 /// Sets the process as traceable, as with `ptrace(PTRACE_TRACEME, ...)`
275 ///
276 /// Indicates that this process is to be traced by its parent.
277 /// This is the only ptrace request to be issued by the tracee.
traceme() -> Result<()>278 pub fn traceme() -> Result<()> {
279 unsafe {
280 ptrace_other(
281 Request::PTRACE_TRACEME,
282 Pid::from_raw(0),
283 ptr::null_mut(),
284 ptr::null_mut(),
285 ).map(drop) // ignore the useless return value
286 }
287 }
288
289 /// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
290 ///
291 /// Arranges for the tracee to be stopped at the next entry to or exit from a system call,
292 /// optionally delivering a signal specified by `sig`.
syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>293 pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
294 let data = match sig.into() {
295 Some(s) => s as i32 as *mut c_void,
296 None => ptr::null_mut(),
297 };
298 unsafe {
299 ptrace_other(
300 Request::PTRACE_SYSCALL,
301 pid,
302 ptr::null_mut(),
303 data,
304 ).map(drop) // ignore the useless return value
305 }
306 }
307
308 /// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)`
309 ///
310 /// In contrast to the `syscall` function, the syscall stopped at will not be executed.
311 /// Thus the the tracee will only be stopped once per syscall,
312 /// optionally delivering a signal specified by `sig`.
313 #[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))]
sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>314 pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
315 let data = match sig.into() {
316 Some(s) => s as i32 as *mut c_void,
317 None => ptr::null_mut(),
318 };
319 unsafe {
320 ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data).map(drop)
321 // ignore the useless return value
322 }
323 }
324
325 /// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
326 ///
327 /// Attaches to the process specified by `pid`, making it a tracee of the calling process.
attach(pid: Pid) -> Result<()>328 pub fn attach(pid: Pid) -> Result<()> {
329 unsafe {
330 ptrace_other(
331 Request::PTRACE_ATTACH,
332 pid,
333 ptr::null_mut(),
334 ptr::null_mut(),
335 ).map(drop) // ignore the useless return value
336 }
337 }
338
339 /// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
340 ///
341 /// Attaches to the process specified in pid, making it a tracee of the calling process.
342 #[cfg(target_os = "linux")]
seize(pid: Pid, options: Options) -> Result<()>343 pub fn seize(pid: Pid, options: Options) -> Result<()> {
344 unsafe {
345 ptrace_other(
346 Request::PTRACE_SEIZE,
347 pid,
348 ptr::null_mut(),
349 options.bits() as *mut c_void,
350 ).map(drop) // ignore the useless return value
351 }
352 }
353
354 /// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)`
355 ///
356 /// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
357 /// signal specified by `sig`.
detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>358 pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
359 let data = match sig.into() {
360 Some(s) => s as i32 as *mut c_void,
361 None => ptr::null_mut(),
362 };
363 unsafe {
364 ptrace_other(
365 Request::PTRACE_DETACH,
366 pid,
367 ptr::null_mut(),
368 data
369 ).map(drop)
370 }
371 }
372
373 /// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
374 ///
375 /// Continues the execution of the process with PID `pid`, optionally
376 /// delivering a signal specified by `sig`.
cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>377 pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
378 let data = match sig.into() {
379 Some(s) => s as i32 as *mut c_void,
380 None => ptr::null_mut(),
381 };
382 unsafe {
383 ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop) // ignore the useless return value
384 }
385 }
386
387 /// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
388 ///
389 /// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
390 #[cfg(target_os = "linux")]
interrupt(pid: Pid) -> Result<()>391 pub fn interrupt(pid: Pid) -> Result<()> {
392 unsafe {
393 ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
394 }
395 }
396
397 /// Issues a kill request as with `ptrace(PTRACE_KILL, ...)`
398 ///
399 /// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);`
kill(pid: Pid) -> Result<()>400 pub fn kill(pid: Pid) -> Result<()> {
401 unsafe {
402 ptrace_other(Request::PTRACE_KILL, pid, ptr::null_mut(), ptr::null_mut()).map(drop)
403 }
404 }
405
406 /// Move the stopped tracee process forward by a single step as with
407 /// `ptrace(PTRACE_SINGLESTEP, ...)`
408 ///
409 /// Advances the execution of the process with PID `pid` by a single step optionally delivering a
410 /// signal specified by `sig`.
411 ///
412 /// # Example
413 /// ```rust
414 /// use nix::sys::ptrace::step;
415 /// use nix::unistd::Pid;
416 /// use nix::sys::signal::Signal;
417 /// use nix::sys::wait::*;
418 ///
419 /// // If a process changes state to the stopped state because of a SIGUSR1
420 /// // signal, this will step the process forward and forward the user
421 /// // signal to the stopped process
422 /// match waitpid(Pid::from_raw(-1), None) {
423 /// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
424 /// let _ = step(pid, Signal::SIGUSR1);
425 /// }
426 /// _ => {},
427 /// }
428 /// ```
step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>429 pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
430 let data = match sig.into() {
431 Some(s) => s as i32 as *mut c_void,
432 None => ptr::null_mut(),
433 };
434 unsafe {
435 ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data).map(drop)
436 }
437 }
438
439 /// Move the stopped tracee process forward by a single step or stop at the next syscall
440 /// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)`
441 ///
442 /// Advances the execution by a single step or until the next syscall.
443 /// In case the tracee is stopped at a syscall, the syscall will not be executed.
444 /// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation.
445 #[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))]
sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>446 pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
447 let data = match sig.into() {
448 Some(s) => s as i32 as *mut c_void,
449 None => ptr::null_mut(),
450 };
451 unsafe {
452 ptrace_other(
453 Request::PTRACE_SYSEMU_SINGLESTEP,
454 pid,
455 ptr::null_mut(),
456 data,
457 )
458 .map(drop) // ignore the useless return value
459 }
460 }
461
462 /// Reads a word from a processes memory at the given address
read(pid: Pid, addr: AddressType) -> Result<c_long>463 pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> {
464 ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut())
465 }
466
467 /// Writes a word into the processes memory at the given address
468 ///
469 /// # Safety
470 ///
471 /// The `data` argument is passed directly to `ptrace(2)`. Read that man page
472 /// for guidance.
write( pid: Pid, addr: AddressType, data: *mut c_void) -> Result<()>473 pub unsafe fn write(
474 pid: Pid,
475 addr: AddressType,
476 data: *mut c_void) -> Result<()>
477 {
478 ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
479 }
480