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