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