• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::errno::Errno;
2 use crate::sys::signal::Signal;
3 use crate::unistd::Pid;
4 use crate::Result;
5 use cfg_if::cfg_if;
6 use libc::{self, c_int};
7 use std::ptr;
8 
9 pub type RequestType = c_int;
10 
11 cfg_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 
24 libc_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 
ptrace_other( request: Request, pid: Pid, addr: AddressType, data: c_int, ) -> Result<c_int>63 unsafe 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.
traceme() -> Result<()>82 pub 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.
attach(pid: Pid) -> Result<()>92 pub 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`.
detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>102 pub 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`.
cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>116 pub 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);`
kill(pid: Pid) -> Result<()>131 pub 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 ))]
step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()>167 pub 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)]
read(pid: Pid, addr: AddressType) -> Result<c_int>181 pub 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)]
write(pid: Pid, addr: AddressType, data: c_int) -> Result<()>193 pub 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