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