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