nix/sys/ptrace/
linux.rs

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