//! Read memory from another Linux or Android process. //! //! `process-reader` is a small `no_std` helper crate for copying raw bytes from a //! target process into caller-provided buffers. It is intended for crash-reporting //! and minidump-writing code that needs to inspect a process without taking a //! dependency on the standard library. //! //! The main entry point is [`ProcessReader`]. A reader can either be created in //! automatic mode with [`ProcessReader::new`], or pinned to one of the supported //! Linux/Android mechanisms: //! //! - [`ProcessReader::for_virtual_mem`] uses `process_vm_readv(2)`. //! - [`ProcessReader::for_file`] uses `/proc//mem`. //! - [`ProcessReader::for_ptrace`] uses `ptrace(PTRACE_PEEKDATA)`. //! //! # Read semantics //! //! [`ProcessReader::read_at`] attempts to copy bytes from a virtual address in the //! target process into the caller's buffer. It returns the number of bytes copied, //! in the range `0..=buf.len()`. That number may be smaller than the buffer //! length. A short successful read is returned as `Ok(n)`, not as an error, and //! callers should only interpret `buf[..n]` as bytes read by the call. //! //! The target process may modify its memory while it is being read. This crate //! does not suspend the target or provide snapshot consistency. //! //! # Strategy selection //! //! [`ProcessReader::new`] tries the strategies in this order: `process_vm_readv`, //! `/proc//mem`, then `ptrace(PTRACE_PEEKDATA)`. The first strategy that //! returns `Ok(_)` for a non-empty request selects the strategy for that reader, //! even if that successful read is shorter than the requested buffer. //! Future reads through the same reader use the selected strategy directly; they //! do not fall back to other strategies if the selected strategy later fails for a //! different address. //! //! This keeps the common path small and predictable, but callers should create a //! new [`ProcessReader`] if they want to retry automatic strategy selection after //! a strategy-specific failure. //! //! # Error model //! //! [`ProcessReader::read_at`] returns [`ReadError`] only when the selected strategy //! reports an error, or when automatic strategy selection cannot get any strategy //! to return a successful read. Short successful reads are reported as `Ok(n)`. //! //! When exactly one strategy failed, [`core::error::Error::source`] returns that //! strategy's lower-level error. When automatic selection fails because every //! strategy failed, there is no single root cause, so `source()` returns `None`. //! Use [`ReadError::virtual_mem_error`], [`ReadError::file_error`], and //! [`ReadError::ptrace_error`] to inspect the individual strategy failures. //! //! The contents of the destination buffer are unspecified after an error. Some //! strategies can fail after writing part of the requested range, and errors do //! not report how many bytes were copied before the failure. //! //! # Platform support //! //! This crate supports Linux and Android. Other operating systems fail to compile. //! The intended Android targets are contemporary Android systems; very old Android //! releases are not part of the supported configuration. //! //! # Example //! //! ```no_run //! # fn example() -> Result<(), process_reader::ReadError> { //! use process_reader::ProcessReader; //! //! let pid = 12345 as libc::pid_t; //! let reader = ProcessReader::new(pid); //! //! let mut bytes = [0u8; 16]; //! let bytes_read = reader.read_at(0x1000, &mut bytes)?; //! let bytes = &bytes[..bytes_read]; //! # let _ = bytes; //! # Ok(()) //! # } //! ``` use self::{error::*, wrapper::*}; use core::{ cell::OnceCell, ffi::{CStr, c_long, c_void}, fmt::Write, mem::size_of, ptr, }; pub use error::{ReadError, ReadExactError}; mod error; mod wrapper; const PTRACE_PEEKDATA_LEN: usize = size_of::(); /// Reads raw bytes from another Linux or Android process. /// /// A `ProcessReader` is bound to a single target process ID. It can either /// choose a read strategy automatically with [`ProcessReader::new`], or be /// constructed with a fixed strategy using [`ProcessReader::for_virtual_mem`], /// [`ProcessReader::for_file`], or [`ProcessReader::for_ptrace`]. /// /// The type does not interpret the bytes it reads. It only copies bytes from the /// target process into the caller's buffer and reports how many bytes were copied. /// /// # Process state and permissions /// /// The operating system still enforces the usual Linux/Android access checks. /// Depending on the chosen strategy, the caller may need suitable ptrace-style /// permissions, ownership, dumpability, capabilities, or an already-stopped /// tracee. /// /// `ProcessReader` does not suspend the target process, attach to it, detach from /// it, or otherwise manage target process lifetime. It also does not provide a /// consistent snapshot if the target process mutates memory while it is being /// read. #[derive(Debug)] pub struct ProcessReader { pid: libc::pid_t, style: OnceCell