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 ®s 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