1 //! For detailed description of the ptrace requests, consult `man ptrace`.
2 
3 use cfg_if::cfg_if;
4 use std::{mem, ptr};
5 use crate::Result;
6 use crate::errno::Errno;
7 use libc::{self, c_void, c_long, siginfo_t};
8 use crate::unistd::Pid;
9 use crate::sys::signal::Signal;
10 
11 pub 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 ))]
19 use libc::user_regs_struct;
20 
21 cfg_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 
32 libc_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 
120 libc_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 
148 libc_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 
ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long>179 fn 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 ))]
getregs(pid: Pid) -> Result<user_regs_struct>197 pub 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 ))]
setregs(pid: Pid, regs: user_regs_struct) -> Result<()>208 pub 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.
ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T>222 fn 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 
ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long>234 unsafe 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,...)`.
setoptions(pid: Pid, options: Options) -> Result<()>239 pub 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,...)`
getevent(pid: Pid) -> Result<c_long>250 pub 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,...)`
getsiginfo(pid: Pid) -> Result<siginfo_t>255 pub 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,...)`
setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()>260 pub 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.
traceme() -> Result<()>278 pub 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`.
syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>293 pub 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")))]
sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>314 pub 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.
attach(pid: Pid) -> Result<()>328 pub 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")]
seize(pid: Pid, options: Options) -> Result<()>343 pub 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`.
detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>358 pub 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`.
cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>377 pub 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")]
interrupt(pid: Pid) -> Result<()>391 pub 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);`
kill(pid: Pid) -> Result<()>400 pub 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 /// ```
step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>429 pub 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")))]
sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>446 pub 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
read(pid: Pid, addr: AddressType) -> Result<c_long>463 pub 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.
write( pid: Pid, addr: AddressType, data: *mut c_void) -> Result<()>473 pub 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 }
480