nix/sys/ptrace/
linux.rs

1//! For detailed description of the ptrace requests, consult `man ptrace`.
2
3use crate::errno::Errno;
4use crate::sys::signal::Signal;
5use crate::unistd::Pid;
6use crate::Result;
7use cfg_if::cfg_if;
8use libc::{self, c_long, c_void, siginfo_t};
9use std::{mem, ptr};
10
11pub 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))]
23use libc::user_regs_struct;
24
25cfg_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
37libc_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
127libc_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
155libc_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
185fn 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))]
212pub 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))]
227pub 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.
243fn 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
257unsafe 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,...)`.
273pub 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,...)`
286pub 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,...)`
291pub 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,...)`
296pub 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.
316pub 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`.
332pub 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))]
353pub 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.
368pub 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())))]
385pub 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`.
401pub 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`.
416pub 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())))]
432pub 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);`
447pub 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/// ```
482pub 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))]
504pub 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
521pub 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.
531pub 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`.
541pub 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.
552pub 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}