13da5c369Sopenharmony_ciuse crate::errno::Errno;
23da5c369Sopenharmony_ciuse crate::sys::signal::Signal;
33da5c369Sopenharmony_ciuse crate::unistd::Pid;
43da5c369Sopenharmony_ciuse crate::Result;
53da5c369Sopenharmony_ciuse cfg_if::cfg_if;
63da5c369Sopenharmony_ciuse libc::{self, c_int};
73da5c369Sopenharmony_ciuse std::ptr;
83da5c369Sopenharmony_ci
93da5c369Sopenharmony_cipub type RequestType = c_int;
103da5c369Sopenharmony_ci
113da5c369Sopenharmony_cicfg_if! {
123da5c369Sopenharmony_ci    if #[cfg(any(target_os = "dragonfly",
133da5c369Sopenharmony_ci                 target_os = "freebsd",
143da5c369Sopenharmony_ci                 target_os = "macos",
153da5c369Sopenharmony_ci                 target_os = "openbsd"))] {
163da5c369Sopenharmony_ci        #[doc(hidden)]
173da5c369Sopenharmony_ci        pub type AddressType = *mut ::libc::c_char;
183da5c369Sopenharmony_ci    } else {
193da5c369Sopenharmony_ci        #[doc(hidden)]
203da5c369Sopenharmony_ci        pub type AddressType = *mut ::libc::c_void;
213da5c369Sopenharmony_ci    }
223da5c369Sopenharmony_ci}
233da5c369Sopenharmony_ci
243da5c369Sopenharmony_cilibc_enum! {
253da5c369Sopenharmony_ci    #[repr(i32)]
263da5c369Sopenharmony_ci    /// Ptrace Request enum defining the action to be taken.
273da5c369Sopenharmony_ci    #[non_exhaustive]
283da5c369Sopenharmony_ci    pub enum Request {
293da5c369Sopenharmony_ci        PT_TRACE_ME,
303da5c369Sopenharmony_ci        PT_READ_I,
313da5c369Sopenharmony_ci        PT_READ_D,
323da5c369Sopenharmony_ci        #[cfg(target_os = "macos")]
333da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
343da5c369Sopenharmony_ci        PT_READ_U,
353da5c369Sopenharmony_ci        PT_WRITE_I,
363da5c369Sopenharmony_ci        PT_WRITE_D,
373da5c369Sopenharmony_ci        #[cfg(target_os = "macos")]
383da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
393da5c369Sopenharmony_ci        PT_WRITE_U,
403da5c369Sopenharmony_ci        PT_CONTINUE,
413da5c369Sopenharmony_ci        PT_KILL,
423da5c369Sopenharmony_ci        #[cfg(any(any(target_os = "dragonfly",
433da5c369Sopenharmony_ci                  target_os = "freebsd",
443da5c369Sopenharmony_ci                  target_os = "macos"),
453da5c369Sopenharmony_ci                  all(target_os = "openbsd", target_arch = "x86_64"),
463da5c369Sopenharmony_ci                  all(target_os = "netbsd", any(target_arch = "x86_64",
473da5c369Sopenharmony_ci                                                target_arch = "powerpc"))))]
483da5c369Sopenharmony_ci        PT_STEP,
493da5c369Sopenharmony_ci        PT_ATTACH,
503da5c369Sopenharmony_ci        PT_DETACH,
513da5c369Sopenharmony_ci        #[cfg(target_os = "macos")]
523da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
533da5c369Sopenharmony_ci        PT_SIGEXC,
543da5c369Sopenharmony_ci        #[cfg(target_os = "macos")]
553da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
563da5c369Sopenharmony_ci        PT_THUPDATE,
573da5c369Sopenharmony_ci        #[cfg(target_os = "macos")]
583da5c369Sopenharmony_ci        #[cfg_attr(docsrs, doc(cfg(all())))]
593da5c369Sopenharmony_ci        PT_ATTACHEXC
603da5c369Sopenharmony_ci    }
613da5c369Sopenharmony_ci}
623da5c369Sopenharmony_ci
633da5c369Sopenharmony_ciunsafe fn ptrace_other(
643da5c369Sopenharmony_ci    request: Request,
653da5c369Sopenharmony_ci    pid: Pid,
663da5c369Sopenharmony_ci    addr: AddressType,
673da5c369Sopenharmony_ci    data: c_int,
683da5c369Sopenharmony_ci) -> Result<c_int> {
693da5c369Sopenharmony_ci    Errno::result(libc::ptrace(
703da5c369Sopenharmony_ci        request as RequestType,
713da5c369Sopenharmony_ci        libc::pid_t::from(pid),
723da5c369Sopenharmony_ci        addr,
733da5c369Sopenharmony_ci        data,
743da5c369Sopenharmony_ci    ))
753da5c369Sopenharmony_ci    .map(|_| 0)
763da5c369Sopenharmony_ci}
773da5c369Sopenharmony_ci
783da5c369Sopenharmony_ci/// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)`
793da5c369Sopenharmony_ci///
803da5c369Sopenharmony_ci/// Indicates that this process is to be traced by its parent.
813da5c369Sopenharmony_ci/// This is the only ptrace request to be issued by the tracee.
823da5c369Sopenharmony_cipub fn traceme() -> Result<()> {
833da5c369Sopenharmony_ci    unsafe {
843da5c369Sopenharmony_ci        ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0)
853da5c369Sopenharmony_ci            .map(drop)
863da5c369Sopenharmony_ci    }
873da5c369Sopenharmony_ci}
883da5c369Sopenharmony_ci
893da5c369Sopenharmony_ci/// Attach to a running process, as with `ptrace(PT_ATTACH, ...)`
903da5c369Sopenharmony_ci///
913da5c369Sopenharmony_ci/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
923da5c369Sopenharmony_cipub fn attach(pid: Pid) -> Result<()> {
933da5c369Sopenharmony_ci    unsafe {
943da5c369Sopenharmony_ci        ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop)
953da5c369Sopenharmony_ci    }
963da5c369Sopenharmony_ci}
973da5c369Sopenharmony_ci
983da5c369Sopenharmony_ci/// Detaches the current running process, as with `ptrace(PT_DETACH, ...)`
993da5c369Sopenharmony_ci///
1003da5c369Sopenharmony_ci/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
1013da5c369Sopenharmony_ci/// signal specified by `sig`.
1023da5c369Sopenharmony_cipub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
1033da5c369Sopenharmony_ci    let data = match sig.into() {
1043da5c369Sopenharmony_ci        Some(s) => s as c_int,
1053da5c369Sopenharmony_ci        None => 0,
1063da5c369Sopenharmony_ci    };
1073da5c369Sopenharmony_ci    unsafe {
1083da5c369Sopenharmony_ci        ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), data).map(drop)
1093da5c369Sopenharmony_ci    }
1103da5c369Sopenharmony_ci}
1113da5c369Sopenharmony_ci
1123da5c369Sopenharmony_ci/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)`
1133da5c369Sopenharmony_ci///
1143da5c369Sopenharmony_ci/// Continues the execution of the process with PID `pid`, optionally
1153da5c369Sopenharmony_ci/// delivering a signal specified by `sig`.
1163da5c369Sopenharmony_cipub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
1173da5c369Sopenharmony_ci    let data = match sig.into() {
1183da5c369Sopenharmony_ci        Some(s) => s as c_int,
1193da5c369Sopenharmony_ci        None => 0,
1203da5c369Sopenharmony_ci    };
1213da5c369Sopenharmony_ci    unsafe {
1223da5c369Sopenharmony_ci        // Ignore the useless return value
1233da5c369Sopenharmony_ci        ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data)
1243da5c369Sopenharmony_ci            .map(drop)
1253da5c369Sopenharmony_ci    }
1263da5c369Sopenharmony_ci}
1273da5c369Sopenharmony_ci
1283da5c369Sopenharmony_ci/// Issues a kill request as with `ptrace(PT_KILL, ...)`
1293da5c369Sopenharmony_ci///
1303da5c369Sopenharmony_ci/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);`
1313da5c369Sopenharmony_cipub fn kill(pid: Pid) -> Result<()> {
1323da5c369Sopenharmony_ci    unsafe {
1333da5c369Sopenharmony_ci        ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop)
1343da5c369Sopenharmony_ci    }
1353da5c369Sopenharmony_ci}
1363da5c369Sopenharmony_ci
1373da5c369Sopenharmony_ci/// Move the stopped tracee process forward by a single step as with
1383da5c369Sopenharmony_ci/// `ptrace(PT_STEP, ...)`
1393da5c369Sopenharmony_ci///
1403da5c369Sopenharmony_ci/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
1413da5c369Sopenharmony_ci/// signal specified by `sig`.
1423da5c369Sopenharmony_ci///
1433da5c369Sopenharmony_ci/// # Example
1443da5c369Sopenharmony_ci/// ```rust
1453da5c369Sopenharmony_ci/// use nix::sys::ptrace::step;
1463da5c369Sopenharmony_ci/// use nix::unistd::Pid;
1473da5c369Sopenharmony_ci/// use nix::sys::signal::Signal;
1483da5c369Sopenharmony_ci/// use nix::sys::wait::*;
1493da5c369Sopenharmony_ci/// // If a process changes state to the stopped state because of a SIGUSR1
1503da5c369Sopenharmony_ci/// // signal, this will step the process forward and forward the user
1513da5c369Sopenharmony_ci/// // signal to the stopped process
1523da5c369Sopenharmony_ci/// match waitpid(Pid::from_raw(-1), None) {
1533da5c369Sopenharmony_ci///     Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => {
1543da5c369Sopenharmony_ci///         let _ = step(pid, Signal::SIGUSR1);
1553da5c369Sopenharmony_ci///     }
1563da5c369Sopenharmony_ci///     _ => {},
1573da5c369Sopenharmony_ci/// }
1583da5c369Sopenharmony_ci/// ```
1593da5c369Sopenharmony_ci#[cfg(any(
1603da5c369Sopenharmony_ci    any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"),
1613da5c369Sopenharmony_ci    all(target_os = "openbsd", target_arch = "x86_64"),
1623da5c369Sopenharmony_ci    all(
1633da5c369Sopenharmony_ci        target_os = "netbsd",
1643da5c369Sopenharmony_ci        any(target_arch = "x86_64", target_arch = "powerpc")
1653da5c369Sopenharmony_ci    )
1663da5c369Sopenharmony_ci))]
1673da5c369Sopenharmony_cipub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
1683da5c369Sopenharmony_ci    let data = match sig.into() {
1693da5c369Sopenharmony_ci        Some(s) => s as c_int,
1703da5c369Sopenharmony_ci        None => 0,
1713da5c369Sopenharmony_ci    };
1723da5c369Sopenharmony_ci    unsafe {
1733da5c369Sopenharmony_ci        ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop)
1743da5c369Sopenharmony_ci    }
1753da5c369Sopenharmony_ci}
1763da5c369Sopenharmony_ci
1773da5c369Sopenharmony_ci/// Reads a word from a processes memory at the given address
1783da5c369Sopenharmony_ci// Technically, ptrace doesn't dereference the pointer.  It passes it directly
1793da5c369Sopenharmony_ci// to the kernel.
1803da5c369Sopenharmony_ci#[allow(clippy::not_unsafe_ptr_arg_deref)]
1813da5c369Sopenharmony_cipub fn read(pid: Pid, addr: AddressType) -> Result<c_int> {
1823da5c369Sopenharmony_ci    unsafe {
1833da5c369Sopenharmony_ci        // Traditionally there was a difference between reading data or
1843da5c369Sopenharmony_ci        // instruction memory but not in modern systems.
1853da5c369Sopenharmony_ci        ptrace_other(Request::PT_READ_D, pid, addr, 0)
1863da5c369Sopenharmony_ci    }
1873da5c369Sopenharmony_ci}
1883da5c369Sopenharmony_ci
1893da5c369Sopenharmony_ci/// Writes a word into the processes memory at the given address
1903da5c369Sopenharmony_ci// Technically, ptrace doesn't dereference the pointer.  It passes it directly
1913da5c369Sopenharmony_ci// to the kernel.
1923da5c369Sopenharmony_ci#[allow(clippy::not_unsafe_ptr_arg_deref)]
1933da5c369Sopenharmony_cipub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> {
1943da5c369Sopenharmony_ci    unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) }
1953da5c369Sopenharmony_ci}
196