xref: /third_party/rust/crates/nix/src/sys/ptrace/bsd.rs (revision 3da5c369)
1use crate::errno::Errno;
2use crate::sys::signal::Signal;
3use crate::unistd::Pid;
4use crate::Result;
5use cfg_if::cfg_if;
6use libc::{self, c_int};
7use std::ptr;
8
9pub type RequestType = c_int;
10
11cfg_if! {
12    if #[cfg(any(target_os = "dragonfly",
13                 target_os = "freebsd",
14                 target_os = "macos",
15                 target_os = "openbsd"))] {
16        #[doc(hidden)]
17        pub type AddressType = *mut ::libc::c_char;
18    } else {
19        #[doc(hidden)]
20        pub type AddressType = *mut ::libc::c_void;
21    }
22}
23
24libc_enum! {
25    #[repr(i32)]
26    /// Ptrace Request enum defining the action to be taken.
27    #[non_exhaustive]
28    pub enum Request {
29        PT_TRACE_ME,
30        PT_READ_I,
31        PT_READ_D,
32        #[cfg(target_os = "macos")]
33        #[cfg_attr(docsrs, doc(cfg(all())))]
34        PT_READ_U,
35        PT_WRITE_I,
36        PT_WRITE_D,
37        #[cfg(target_os = "macos")]
38        #[cfg_attr(docsrs, doc(cfg(all())))]
39        PT_WRITE_U,
40        PT_CONTINUE,
41        PT_KILL,
42        #[cfg(any(any(target_os = "dragonfly",
43                  target_os = "freebsd",
44                  target_os = "macos"),
45                  all(target_os = "openbsd", target_arch = "x86_64"),
46                  all(target_os = "netbsd", any(target_arch = "x86_64",
47                                                target_arch = "powerpc"))))]
48        PT_STEP,
49        PT_ATTACH,
50        PT_DETACH,
51        #[cfg(target_os = "macos")]
52        #[cfg_attr(docsrs, doc(cfg(all())))]
53        PT_SIGEXC,
54        #[cfg(target_os = "macos")]
55        #[cfg_attr(docsrs, doc(cfg(all())))]
56        PT_THUPDATE,
57        #[cfg(target_os = "macos")]
58        #[cfg_attr(docsrs, doc(cfg(all())))]
59        PT_ATTACHEXC
60    }
61}
62
63unsafe fn ptrace_other(
64    request: Request,
65    pid: Pid,
66    addr: AddressType,
67    data: c_int,
68) -> Result<c_int> {
69    Errno::result(libc::ptrace(
70        request as RequestType,
71        libc::pid_t::from(pid),
72        addr,
73        data,
74    ))
75    .map(|_| 0)
76}
77
78/// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
79///
80/// Indicates that this process is to be traced by its parent.
81/// This is the only ptrace request to be issued by the tracee.
82pub fn traceme() -> Result<()> {
83    unsafe {
84        ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0)
85            .map(drop)
86    }
87}
88
89/// Attach to a running process, as with `ptrace(PT_ATTACH, ...)`
90///
91/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
92pub fn attach(pid: Pid) -> Result<()> {
93    unsafe {
94        ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop)
95    }
96}
97
98/// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
99///
100/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
101/// signal specified by `sig`.
102pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
103    let data = match sig.into() {
104        Some(s) => s as c_int,
105        None => 0,
106    };
107    unsafe {
108        ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop)
109    }
110}
111
112/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
113///
114/// Continues the execution of the process with PID `pid`, optionally
115/// delivering a signal specified by `sig`.
116pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
117    let data = match sig.into() {
118        Some(s) => s as c_int,
119        None => 0,
120    };
121    unsafe {
122        // Ignore the useless return value
123        ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data)
124            .map(drop)
125    }
126}
127
128/// Issues a kill request as with `ptrace(PT_KILL, ...)`
129///
130/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
131pub fn kill(pid: Pid) -> Result<()> {
132    unsafe {
133        ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
134    }
135}
136
137/// Move the stopped tracee process forward by a single step as with
138/// `ptrace(PT_STEP, ...)`
139///
140/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
141/// signal specified by `sig`.
142///
143/// # Example
144/// ```rust
145/// use nix::sys::ptrace::step;
146/// use nix::unistd::Pid;
147/// use nix::sys::signal::Signal;
148/// use nix::sys::wait::*;
149/// // If a process changes state to the stopped state because of a SIGUSR1
150/// // signal, this will step the process forward and forward the user
151/// // signal to the stopped process
152/// match waitpid(Pid::from_raw(-1), None) {
153///     Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
154///         let _ = step(pid, Signal::SIGUSR1);
155///     }
156///     _ => {},
157/// }
158/// ```
159#[cfg(any(
160    any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
161    all(target_os = "openbsd", target_arch = "x86_64"),
162    all(
163        target_os = "netbsd",
164        any(target_arch = "x86_64", target_arch = "powerpc")
165    )
166))]
167pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
168    let data = match sig.into() {
169        Some(s) => s as c_int,
170        None => 0,
171    };
172    unsafe {
173        ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop)
174    }
175}
176
177/// Reads a word from a processes memory at the given address
178// Technically, ptrace doesn't dereference the pointer.  It passes it directly
179// to the kernel.
180#[allow(clippy::not_unsafe_ptr_arg_deref)]
181pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
182    unsafe {
183        // Traditionally there was a difference between reading data or
184        // instruction memory but not in modern systems.
185        ptrace_other(Request::PT_READ_D, pid, addr, 0)
186    }
187}
188
189/// Writes a word into the processes memory at the given address
190// Technically, ptrace doesn't dereference the pointer.  It passes it directly
191// to the kernel.
192#[allow(clippy::not_unsafe_ptr_arg_deref)]
193pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
194    unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
195}
196