8000 linux: add memory-reading helpers for reading POD values from a process by schopin-mozilla · Pull Request #220 · rust-minidump/minidump-writer · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ memmap2 = "0.9"
byteorder = "1.4"
error-graph = { version = "0.1.1", features = ["serde"] }
failspot = "0.2.0"
plain = { version = "0.2.3", default-features = false }
process-backend = { version = "0.1.0", path = "crates/linux/process-backend", features = ["testing"] }

# Used for parsing procfs info.
Expand Down
2 changes: 2 additions & 0 deletions src/linux/process_inspection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ pub enum Error {
AddressOverflowed,
#[error("unexpected end of buffer reached")]
UnexpectedEndOfBuffer,
#[error("got an unexpected end of file")]
UnexpectedEndOfFile,
}

fn set_process_backend_drop_fail_handler() {
Expand Down
119 changes: 119 additions & 0 deletions src/linux/process_inspection/process_reader.rs
< 8000 th scope="col">Diff line change
Original file line number Diff line number
Expand Up @@ -4,6 +4,8 @@ use super::{
};
use crate::module_reader::ProcessModuleMemoryReader;

use plain::Plain;

pub type ProcessHandle = libc::pid_t;

#[derive(Debug)]
Expand All @@ -16,6 +18,123 @@ impl<'a> ProcessReader<'a> {
pub fn read(&self, src: usize, dst: &mut [u8]) -> Result<usize, CopyFromProcessError> {
self.0.read_at(src, dst)
}

/// Read memory from the process until the buffer is filled.
///
/// This is a convenience wrapper around [`ProcessReader::read`]. Unlike
/// [`read`](Self::read), this method does not return successful short
/// reads. It repeatedly calls [`read`](Self::read), advancing `address`
/// and the output buffer by the number of bytes read, until the entire buffer
/// has been filled.
///
/// If an underlying read fails before the buffer is filled, this method returns
/// that error as is. If an underlying read succeeds but returns `0`
/// bytes before the buffer is filled, this method returns
/// [`CopyFromProcessError::Backend`] containing [`Error::UnexpectedEndOfFile`].
///
/// On success, all of `buf` has been filled with bytes read from the target
/// process.
///
/// # Errors
///
/// Returns whichever error [`read`](Self::read) would return.
///
/// In addition, returns [`CopyFromProcessError::Backend`] containing
/// [`Error::UnexpectedEndOfFile`] if an underlying read returns `0` bytes
/// before the buffer is full, and [`CopyFromProcessError::Backend`] containing
/// [`Error::AddressOverflowed`] if advancing the read address by the number of
/// bytes read would wrap past the end of the address space.
///
/// If this method returns an error, `buf` may have been partially overwritten.
/// The error type does not report how many bytes were read before the failure.
pub fn read_exact(
&self,
mut src: usize,
mut dst: &mut [u8],
) -> Result<(), CopyFromProcessError> {
if dst.is_empty() {
return Ok(());
}

loop {
let bytes_read = self.read(src, dst)?;
if bytes_read == 0 {
return Err(CopyFromProcessError::Backend(Error::UnexpectedEndOfFile));
}
if bytes_read == dst.len() {
return Ok(());
}
src = src
.checked_add(bytes_read)
.ok_or(CopyFromProcessError::Backend(Error::AddressOverflowed))?;
dst = &mut dst[bytes_read..];
}
}

/// Reads a plain-old-data value of type `T` from the target process at
/// `address`.
///
/// This reads exactly `size_of::<T>()` bytes into a freshly zeroed `T` using
/// [`read_exact`](Self::read_exact). The [`Plain`] bound guarantees that
/// every possible bit pattern is a valid `T`, so the bytes read from the
/// target process are always a valid value.
///
/// # Errors
///
/// See [`read_exact`](Self::read_exact) for the possible errors.
pub fn read_pod<T: Plain>(&self, address: usize) -> Result<T, CopyFromProcessError> {
// Safety: `Plain` is an unsafe trait that may only be implemented on
// types for which every possible bit pattern is valid, so there is
// nothing we could read from the other process that isn't a valid value
// for `T`.
let mut pod: T = unsafe { core::mem::zeroed() };
let bytes = unsafe {
core::slice::from_raw_parts_mut(
core::ptr::from_mut(&mut pod).cast::<u8>(),
size_of::<T>(),
)
};
self.read_exact(address, bytes)?;
Ok(pod)
}

/// Read `count` consecutive plain-old-data values of type `T` starting at
/// `address`.
pub fn read_pod_vec<T: Plain>(
&self,
mut address: usize,
count: usize,
) -> Result<Vec<T>, CopyFromProcessError> {
let mut v = Vec::with_capacity(count);
for _ in 0..count {
v.push(self.read_pod(address)?);
address += std::mem::size_of::<T>();
}
Ok(v)
}

/// Read bytes from the process starting at `address` into `buf` up to and
/// including the first `terminator` byte (or until a read returns no bytes).
///
/// Returns the number of bytes appended to `buf`.
pub fn read_until(
&self,
mut address: usize,
terminator: u8,
buf: &mut Vec<u8>,
) -> Result<usize, CopyFromProcessError> {
let start_len = buf.len();
let mut b = [0u8];
while self.read(address, &mut b)? > 0 {
buf.push(b[0]);
if b[0] == terminator {
break;
}
address += 1;
}
Ok(buf.len() - start_len)
}

/// Find the address at which a module with the given name is loaded in the process.
pub fn find_module(
&self,
Expand Down
Loading
0