Skip to main content

proot/tracee/
ptrace.rs

1//! Thin ptrace wrappers.  All requests must come from the tracer thread.
2
3use crate::sys::{Result, check};
4
5/// Options the tracer sets on every tracee.
6pub const DEFAULT_OPTIONS: i64 = (libc::PTRACE_O_TRACESYSGOOD
7    | libc::PTRACE_O_TRACEFORK
8    | libc::PTRACE_O_TRACEVFORK
9    | libc::PTRACE_O_TRACECLONE
10    | libc::PTRACE_O_TRACEEXEC
11    | libc::PTRACE_O_TRACESECCOMP) as i64;
12
13/// `PTRACE_O_EXITKILL`, added when `--kill-on-exit` is set.
14pub const OPTION_EXITKILL: i64 = libc::PTRACE_O_EXITKILL as i64;
15
16/// Attaches to `pid` with `PTRACE_SEIZE`, setting `options` at the same time.
17///
18/// Unlike `PTRACE_ATTACH`/`PTRACE_TRACEME`, seizing does not stop the tracee
19/// and makes group-stops and new-child stops arrive as `PTRACE_EVENT_STOP`,
20/// which is what lets the tracer tell them apart from signal delivery.
21///
22/// # Arguments
23/// * `pid` - Process to seize; must be our child or otherwise ptrace-accessible.
24/// * `options` - Bitwise or of `PTRACE_O_*`.
25pub fn seize(pid: i32, options: i64) -> Result<()> {
26    check(unsafe { libc::ptrace(libc::PTRACE_SEIZE, pid, 0, options) }).map(|_| ())
27}
28
29/// Leaves a group-stopped tracee stopped but able to report further events.
30pub fn listen(pid: i32) -> Result<()> {
31    check(unsafe { libc::ptrace(libc::PTRACE_LISTEN, pid, 0, 0) }).map(|_| ())
32}
33
34#[allow(dead_code)]
35/// Sets ptrace options on a stopped tracee.
36///
37/// # Arguments
38/// * `pid` - Tracee thread id.
39/// * `options` - Bitwise or of `PTRACE_O_*`.
40pub fn setoptions(pid: i32, options: i64) -> Result<()> {
41    check(unsafe { libc::ptrace(libc::PTRACE_SETOPTIONS, pid, 0, options) }).map(|_| ())
42}
43
44/// Resumes a stopped tracee, delivering `sig` (0 for none).
45pub fn cont(pid: i32, sig: i32) -> Result<()> {
46    check(unsafe { libc::ptrace(libc::PTRACE_CONT, pid, 0, sig as i64) }).map(|_| ())
47}
48
49/// Reads the `siginfo` of the signal a tracee is stopped for.
50pub fn getsiginfo(pid: i32) -> Result<libc::siginfo_t> {
51    let mut si: libc::siginfo_t = unsafe { std::mem::zeroed() };
52    check(unsafe { libc::ptrace(libc::PTRACE_GETSIGINFO, pid, 0, &mut si as *mut _) })?;
53    Ok(si)
54}
55
56/// Reads the event message of the current stop (new child pid, exit status...).
57pub fn geteventmsg(pid: i32) -> Result<u64> {
58    let mut msg: u64 = 0;
59    check(unsafe { libc::ptrace(libc::PTRACE_GETEVENTMSG, pid, 0, &mut msg as *mut u64) })?;
60    Ok(msg)
61}
62
63/// Fetches the general purpose registers of a stopped tracee (x86 only).
64#[cfg(target_arch = "x86_64")]
65pub fn getregs(pid: i32) -> Result<libc::user_regs_struct> {
66    let mut regs: libc::user_regs_struct = unsafe { std::mem::zeroed() };
67    check(unsafe { libc::ptrace(libc::PTRACE_GETREGS, pid, 0, &mut regs as *mut _) })?;
68    Ok(regs)
69}
70
71/// Stores the general purpose registers of a stopped tracee (x86 only).
72#[cfg(target_arch = "x86_64")]
73pub fn setregs(pid: i32, regs: &libc::user_regs_struct) -> Result<()> {
74    check(unsafe { libc::ptrace(libc::PTRACE_SETREGS, pid, 0, regs as *const _) }).map(|_| ())
75}
76
77/// Reads a register set (`PTRACE_GETREGSET`), the only register interface on aarch64.
78///
79/// # Arguments
80/// * `pid` - Tracee thread id.
81/// * `nt` - Note type, e.g. `NT_PRSTATUS`.
82/// * `buf` - Destination of `len` bytes.
83///
84/// # Returns
85/// The number of bytes the kernel filled: a 32-bit tracee's `NT_PRSTATUS` is
86/// smaller than a 64-bit one, which is how the ABI is told apart.
87#[allow(dead_code)]
88pub fn getregset(pid: i32, nt: u32, buf: *mut u8, len: usize) -> Result<usize> {
89    let mut iov = libc::iovec {
90        iov_base: buf as *mut _,
91        iov_len: len,
92    };
93    check(unsafe { libc::ptrace(libc::PTRACE_GETREGSET, pid, nt as usize, &mut iov as *mut _) })?;
94    Ok(iov.iov_len)
95}
96
97/// Writes a register set (`PTRACE_SETREGSET`).
98///
99/// # Arguments
100/// * `pid` - Tracee thread id.
101/// * `nt` - Note type, e.g. `NT_PRSTATUS` or `NT_ARM_SYSTEM_CALL`.
102/// * `buf` - Source of `len` bytes.
103#[allow(dead_code)]
104pub fn setregset(pid: i32, nt: u32, buf: *mut u8, len: usize) -> Result<()> {
105    let mut iov = libc::iovec {
106        iov_base: buf as *mut _,
107        iov_len: len,
108    };
109    check(unsafe { libc::ptrace(libc::PTRACE_SETREGSET, pid, nt as usize, &mut iov as *mut _) }).map(|_| ())
110}