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