xref: /third_party/rust/crates/nix/src/sys/ptrace/linux.rs (revision 3da5c369)
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", target_env = "ohos")
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 = "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
133libc_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
161libc_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
191fn 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))]
218pub 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))]
233pub 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            &regs 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.
249fn 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
263unsafe 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,...)`.
279pub 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,...)`
292pub 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,...)`
297pub 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,...)`
302pub 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.
322pub 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`.
338pub 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))]
359pub 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.
374pub 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())))]
391pub 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`.
407pub 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`.
422pub 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())))]
438pub 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);`
453pub 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/// ```
488pub 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))]
510pub 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
527pub 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.
537pub 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`.
547pub 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.
558pub 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