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