use crate::errno::Errno; use crate::sys::signal::Signal; use crate::unistd::Pid; use crate::Result; use cfg_if::cfg_if; use libc::{self, c_int}; use std::ptr; pub type RequestType = c_int; cfg_if! { if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos", target_os = "openbsd"))] { #[doc(hidden)] pub type AddressType = *mut ::libc::c_char; } else { #[doc(hidden)] pub type AddressType = *mut ::libc::c_void; } } libc_enum! { #[repr(i32)] /// Ptrace Request enum defining the action to be taken. #[non_exhaustive] pub enum Request { PT_TRACE_ME, PT_READ_I, PT_READ_D, #[cfg(target_os = "macos")] #[cfg_attr(docsrs, doc(cfg(all())))] PT_READ_U, PT_WRITE_I, PT_WRITE_D, #[cfg(target_os = "macos")] #[cfg_attr(docsrs, doc(cfg(all())))] PT_WRITE_U, PT_CONTINUE, PT_KILL, #[cfg(any(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"), all(target_os = "openbsd", target_arch = "x86_64"), all(target_os = "netbsd", any(target_arch = "x86_64", target_arch = "powerpc"))))] PT_STEP, PT_ATTACH, PT_DETACH, #[cfg(target_os = "macos")] #[cfg_attr(docsrs, doc(cfg(all())))] PT_SIGEXC, #[cfg(target_os = "macos")] #[cfg_attr(docsrs, doc(cfg(all())))] PT_THUPDATE, #[cfg(target_os = "macos")] #[cfg_attr(docsrs, doc(cfg(all())))] PT_ATTACHEXC } } unsafe fn ptrace_other( request: Request, pid: Pid, addr: AddressType, data: c_int, ) -> Result { Errno::result(libc::ptrace( request as RequestType, libc::pid_t::from(pid), addr, data, )) .map(|_| 0) } /// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)` /// /// Indicates that this process is to be traced by its parent. /// This is the only ptrace request to be issued by the tracee. pub fn traceme() -> Result<()> { unsafe { ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0) .map(drop) } } /// Attach to a running process, as with `ptrace(PT_ATTACH, ...)` /// /// Attaches to the process specified by `pid`, making it a tracee of the calling process. pub fn attach(pid: Pid) -> Result<()> { unsafe { ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop) } } /// Detaches the current running process, as with `ptrace(PT_DETACH, ...)` /// /// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a /// signal specified by `sig`. pub fn detach>>(pid: Pid, sig: T) -> Result<()> { let data = match sig.into() { Some(s) => s as c_int, None => 0, }; unsafe { ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop) } } /// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)` /// /// Continues the execution of the process with PID `pid`, optionally /// delivering a signal specified by `sig`. pub fn cont>>(pid: Pid, sig: T) -> Result<()> { let data = match sig.into() { Some(s) => s as c_int, None => 0, }; unsafe { // Ignore the useless return value ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data) .map(drop) } } /// Issues a kill request as with `ptrace(PT_KILL, ...)` /// /// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);` pub fn kill(pid: Pid) -> Result<()> { unsafe { ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop) } } /// Move the stopped tracee process forward by a single step as with /// `ptrace(PT_STEP, ...)` /// /// Advances the execution of the process with PID `pid` by a single step optionally delivering a /// signal specified by `sig`. /// /// # Example /// ```rust /// use nix::sys::ptrace::step; /// use nix::unistd::Pid; /// use nix::sys::signal::Signal; /// use nix::sys::wait::*; /// // If a process changes state to the stopped state because of a SIGUSR1 /// // signal, this will step the process forward and forward the user /// // signal to the stopped process /// match waitpid(Pid::from_raw(-1), None) { /// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => { /// let _ = step(pid, Signal::SIGUSR1); /// } /// _ => {}, /// } /// ``` #[cfg(any( any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"), all(target_os = "openbsd", target_arch = "x86_64"), all( target_os = "netbsd", any(target_arch = "x86_64", target_arch = "powerpc") ) ))] pub fn step>>(pid: Pid, sig: T) -> Result<()> { let data = match sig.into() { Some(s) => s as c_int, None => 0, }; unsafe { ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop) } } /// Reads a word from a processes memory at the given address // Technically, ptrace doesn't dereference the pointer. It passes it directly // to the kernel. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn read(pid: Pid, addr: AddressType) -> Result { unsafe { // Traditionally there was a difference between reading data or // instruction memory but not in modern systems. ptrace_other(Request::PT_READ_D, pid, addr, 0) } } /// Writes a word into the processes memory at the given address // Technically, ptrace doesn't dereference the pointer. It passes it directly // to the kernel. #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> { unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) } }