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