//! For detailed description of the ptrace requests, consult `man ptrace`. use cfg_if::cfg_if; use std::{mem, ptr}; use crate::Result; use crate::errno::Errno; use libc::{self, c_void, c_long, siginfo_t}; use crate::unistd::Pid; use crate::sys::signal::Signal; pub type AddressType = *mut ::libc::c_void; #[cfg(all( target_os = "linux", any(all(target_arch = "x86_64", any(target_env = "gnu", target_env = "musl")), all(target_arch = "x86", target_env = "gnu")) ))] use libc::user_regs_struct; cfg_if! { if #[cfg(any(all(target_os = "linux", target_arch = "s390x"), all(target_os = "linux", target_env = "gnu")))] { #[doc(hidden)] pub type RequestType = ::libc::c_uint; } else { #[doc(hidden)] pub type RequestType = ::libc::c_int; } } libc_enum!{ #[cfg_attr(not(any(target_env = "musl", target_os = "android")), repr(u32))] #[cfg_attr(any(target_env = "musl", target_os = "android"), repr(i32))] /// Ptrace Request enum defining the action to be taken. #[non_exhaustive] pub enum Request { PTRACE_TRACEME, PTRACE_PEEKTEXT, PTRACE_PEEKDATA, PTRACE_PEEKUSER, PTRACE_POKETEXT, PTRACE_POKEDATA, PTRACE_POKEUSER, PTRACE_CONT, PTRACE_KILL, PTRACE_SINGLESTEP, #[cfg(any(all(target_os = "android", target_pointer_width = "32"), all(target_os = "linux", any(target_env = "musl", target_arch = "mips", target_arch = "mips64", target_arch = "x86_64", target_pointer_width = "32"))))] PTRACE_GETREGS, #[cfg(any(all(target_os = "android", target_pointer_width = "32"), all(target_os = "linux", any(target_env = "musl", target_arch = "mips", target_arch = "mips64", target_arch = "x86_64", target_pointer_width = "32"))))] PTRACE_SETREGS, #[cfg(any(all(target_os = "android", target_pointer_width = "32"), all(target_os = "linux", any(target_env = "musl", target_arch = "mips", target_arch = "mips64", target_arch = "x86_64", target_pointer_width = "32"))))] PTRACE_GETFPREGS, #[cfg(any(all(target_os = "android", target_pointer_width = "32"), all(target_os = "linux", any(target_env = "musl", target_arch = "mips", target_arch = "mips64", target_arch = "x86_64", target_pointer_width = "32"))))] PTRACE_SETFPREGS, PTRACE_ATTACH, PTRACE_DETACH, #[cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "mips", target_arch = "mips64", target_arch = "x86", target_arch = "x86_64")))] PTRACE_GETFPXREGS, #[cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "mips", target_arch = "mips64", target_arch = "x86", target_arch = "x86_64")))] PTRACE_SETFPXREGS, PTRACE_SYSCALL, PTRACE_SETOPTIONS, PTRACE_GETEVENTMSG, PTRACE_GETSIGINFO, PTRACE_SETSIGINFO, #[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))] PTRACE_GETREGSET, #[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))] PTRACE_SETREGSET, #[cfg(target_os = "linux")] PTRACE_SEIZE, #[cfg(target_os = "linux")] PTRACE_INTERRUPT, #[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))] PTRACE_LISTEN, #[cfg(all(target_os = "linux", not(any(target_arch = "mips", target_arch = "mips64"))))] PTRACE_PEEKSIGINFO, #[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))] PTRACE_SYSEMU, #[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))] PTRACE_SYSEMU_SINGLESTEP, } } libc_enum!{ #[repr(i32)] /// Using the ptrace options the tracer can configure the tracee to stop /// at certain events. This enum is used to define those events as defined /// in `man ptrace`. #[non_exhaustive] pub enum Event { /// Event that stops before a return from fork or clone. PTRACE_EVENT_FORK, /// Event that stops before a return from vfork or clone. PTRACE_EVENT_VFORK, /// Event that stops before a return from clone. PTRACE_EVENT_CLONE, /// Event that stops before a return from execve. PTRACE_EVENT_EXEC, /// Event for a return from vfork. PTRACE_EVENT_VFORK_DONE, /// Event for a stop before an exit. Unlike the waitpid Exit status program. /// registers can still be examined PTRACE_EVENT_EXIT, /// STop triggered by a seccomp rule on a tracee. PTRACE_EVENT_SECCOMP, // PTRACE_EVENT_STOP not provided by libc because it's defined in glibc 2.26 } } libc_bitflags! { /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request. /// See `man ptrace` for more details. pub struct Options: libc::c_int { /// When delivering system call traps set a bit to allow tracer to /// distinguish between normal stops or syscall stops. May not work on /// all systems. PTRACE_O_TRACESYSGOOD; /// Stop tracee at next fork and start tracing the forked process. PTRACE_O_TRACEFORK; /// Stop tracee at next vfork call and trace the vforked process. PTRACE_O_TRACEVFORK; /// Stop tracee at next clone call and trace the cloned process. PTRACE_O_TRACECLONE; /// Stop tracee at next execve call. PTRACE_O_TRACEEXEC; /// Stop tracee at vfork completion. PTRACE_O_TRACEVFORKDONE; /// Stop tracee at next exit call. Stops before exit commences allowing /// tracer to see location of exit and register states. PTRACE_O_TRACEEXIT; /// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more /// details. PTRACE_O_TRACESECCOMP; /// Send a SIGKILL to the tracee if the tracer exits. This is useful /// for ptrace jailers to prevent tracees from escaping their control. #[cfg(any(target_os = "android", target_os = "linux"))] PTRACE_O_EXITKILL; } } fn ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result { let ret = unsafe { Errno::clear(); libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data) }; match Errno::result(ret) { Ok(..) | Err(Errno::UnknownErrno) => Ok(ret), err @ Err(..) => err, } } /// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)` #[cfg(all( target_os = "linux", any(all(target_arch = "x86_64", any(target_env = "gnu", target_env = "musl")), all(target_arch = "x86", target_env = "gnu")) ))] pub fn getregs(pid: Pid) -> Result { ptrace_get_data::(Request::PTRACE_GETREGS, pid) } /// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)` #[cfg(all( target_os = "linux", any(all(target_arch = "x86_64", any(target_env = "gnu", target_env = "musl")), all(target_arch = "x86", target_env = "gnu")) ))] pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> { let res = unsafe { libc::ptrace(Request::PTRACE_SETREGS as RequestType, libc::pid_t::from(pid), ptr::null_mut::(), ®s as *const _ as *const c_void) }; Errno::result(res).map(drop) } /// Function for ptrace requests that return values from the data field. /// Some ptrace get requests populate structs or larger elements than `c_long` /// and therefore use the data field to return values. This function handles these /// requests. fn ptrace_get_data(request: Request, pid: Pid) -> Result { let mut data = mem::MaybeUninit::uninit(); let res = unsafe { libc::ptrace(request as RequestType, libc::pid_t::from(pid), ptr::null_mut::(), data.as_mut_ptr() as *const _ as *const c_void) }; Errno::result(res)?; Ok(unsafe{ data.assume_init() }) } unsafe fn ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result { Errno::result(libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)).map(|_| 0) } /// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`. pub fn setoptions(pid: Pid, options: Options) -> Result<()> { let res = unsafe { libc::ptrace(Request::PTRACE_SETOPTIONS as RequestType, libc::pid_t::from(pid), ptr::null_mut::(), options.bits() as *mut c_void) }; Errno::result(res).map(drop) } /// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)` pub fn getevent(pid: Pid) -> Result { ptrace_get_data::(Request::PTRACE_GETEVENTMSG, pid) } /// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)` pub fn getsiginfo(pid: Pid) -> Result { ptrace_get_data::(Request::PTRACE_GETSIGINFO, pid) } /// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)` pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> { let ret = unsafe{ Errno::clear(); libc::ptrace(Request::PTRACE_SETSIGINFO as RequestType, libc::pid_t::from(pid), ptr::null_mut::(), sig as *const _ as *const c_void) }; match Errno::result(ret) { Ok(_) => Ok(()), Err(e) => Err(e), } } /// Sets the process as traceable, as with `ptrace(PTRACE_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::PTRACE_TRACEME, Pid::from_raw(0), ptr::null_mut(), ptr::null_mut(), ).map(drop) // ignore the useless return value } } /// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)` /// /// Arranges for the tracee to be stopped at the next entry to or exit from a system call, /// optionally delivering a signal specified by `sig`. pub fn syscall>>(pid: Pid, sig: T) -> Result<()> { let data = match sig.into() { Some(s) => s as i32 as *mut c_void, None => ptr::null_mut(), }; unsafe { ptrace_other( Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data, ).map(drop) // ignore the useless return value } } /// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)` /// /// In contrast to the `syscall` function, the syscall stopped at will not be executed. /// Thus the the tracee will only be stopped once per syscall, /// optionally delivering a signal specified by `sig`. #[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))] pub fn sysemu>>(pid: Pid, sig: T) -> Result<()> { let data = match sig.into() { Some(s) => s as i32 as *mut c_void, None => ptr::null_mut(), }; unsafe { ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data).map(drop) // ignore the useless return value } } /// Attach to a running process, as with `ptrace(PTRACE_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::PTRACE_ATTACH, pid, ptr::null_mut(), ptr::null_mut(), ).map(drop) // ignore the useless return value } } /// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)` /// /// Attaches to the process specified in pid, making it a tracee of the calling process. #[cfg(target_os = "linux")] pub fn seize(pid: Pid, options: Options) -> Result<()> { unsafe { ptrace_other( Request::PTRACE_SEIZE, pid, ptr::null_mut(), options.bits() as *mut c_void, ).map(drop) // ignore the useless return value } } /// Detaches the current running process, as with `ptrace(PTRACE_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 i32 as *mut c_void, None => ptr::null_mut(), }; unsafe { ptrace_other( Request::PTRACE_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 i32 as *mut c_void, None => ptr::null_mut(), }; unsafe { ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop) // ignore the useless return value } } /// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)` /// /// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)` #[cfg(target_os = "linux")] pub fn interrupt(pid: Pid) -> Result<()> { unsafe { ptrace_other(Request::PTRACE_INTERRUPT, pid, ptr::null_mut(), ptr::null_mut()).map(drop) } } /// Issues a kill request as with `ptrace(PTRACE_KILL, ...)` /// /// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);` pub fn kill(pid: Pid) -> Result<()> { unsafe { ptrace_other(Request::PTRACE_KILL, pid, ptr::null_mut(), ptr::null_mut()).map(drop) } } /// Move the stopped tracee process forward by a single step as with /// `ptrace(PTRACE_SINGLESTEP, ...)` /// /// 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::*; /// fn main() { /// // 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); /// } /// _ => {}, /// } /// } /// ``` pub fn step>>(pid: Pid, sig: T) -> Result<()> { let data = match sig.into() { Some(s) => s as i32 as *mut c_void, None => ptr::null_mut(), }; unsafe { ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data).map(drop) } } /// Move the stopped tracee process forward by a single step or stop at the next syscall /// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)` /// /// Advances the execution by a single step or until the next syscall. /// In case the tracee is stopped at a syscall, the syscall will not be executed. /// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation. #[cfg(all(target_os = "linux", target_env = "gnu", any(target_arch = "x86", target_arch = "x86_64")))] pub fn sysemu_step>>(pid: Pid, sig: T) -> Result<()> { let data = match sig.into() { Some(s) => s as i32 as *mut c_void, None => ptr::null_mut(), }; unsafe { ptrace_other( Request::PTRACE_SYSEMU_SINGLESTEP, pid, ptr::null_mut(), data, ) .map(drop) // ignore the useless return value } } /// Reads a word from a processes memory at the given address pub fn read(pid: Pid, addr: AddressType) -> Result { ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut()) } /// Writes a word into the processes memory at the given address /// /// # Safety /// /// The `data` argument is passed directly to `ptrace(2)`. Read that man page /// for guidance. pub unsafe fn write( pid: Pid, addr: AddressType, data: *mut c_void) -> Result<()> { ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) }