diff --git a/crates/linux/process-backend/src/lib.rs b/crates/linux/process-backend/src/lib.rs index d52ec9b6..014394c9 100644 --- a/crates/linux/process-backend/src/lib.rs +++ b/crates/linux/process-backend/src/lib.rs @@ -1,6 +1,9 @@ #![no_std] #![cfg(any(target_os = "linux", target_os = "android"))] +use core::ffi::CStr; +use regs::*; + pub use drop_fail_handler::Handler as DropFailHandler; #[macro_use] @@ -11,9 +14,73 @@ pub mod regs; mod wrapper; +pub type Result = core::result::Result; + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pub const PTRACE_DATA_LEN: usize = core::mem::size_of::(); +pub trait Backend: core::fmt::Debug { + type ProcessReader<'a>: ProcessReader + where + Self: 'a; + type FileReader<'a>: FileReader + where + Self: 'a; + type DirReader<'a>: DirReader + where + Self: 'a; + type MappedModuleMemoryReader<'a>: MappedModuleMemoryReader + where + Self: 'a; + + fn pid(&self) -> Result; + fn process_reader<'a>(&'a self) -> Self::ProcessReader<'a>; + fn stop_process(&self) -> Result<()>; + fn continue_process(&self) -> Result<()>; + fn suspend_thread(&self, tid: libc::pid_t) -> Result<()>; + fn resume_thread(&self, tid: libc::pid_t) -> Result<()>; + fn map_module_into_memory<'a>( + &'a self, + path: &CStr, + offset: u64, + ) -> Result>; + fn stat_file(&self, path: &CStr) -> Result; + fn read_file<'a>(&'a self, path: &CStr) -> Result>; + fn read_dir<'a>(&'a self, path: &CStr) -> Result>; + fn read_link(&self, path: &CStr, buf: &mut [u8]) -> Result; + fn get_gen_regs(&self, tid: libc::pid_t) -> Result; + fn get_fp_regs(&self, tid: libc::pid_t) -> Result; + + #[cfg(target_arch = "x86")] + fn get_fpx_regs(&self, tid: libc::pid_t) -> Result; + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn ptrace_peekuser(&self, addr: usize) -> Result<[u8; PTRACE_DATA_LEN]>; + + fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()>; + + #[cfg(feature = "testing")] + fn fail_one_syscall_with(&self, errno: core::ffi::c_int); +} + +pub trait FileReader: core::fmt::Debug { + fn read(&mut self, buf: &mut [u8]) -> Result; +} + +pub trait DirReader: core::fmt::Debug { + fn read_next_name(&mut self, buf: &mut [u8]) -> Result; +} + +pub trait ProcessReader: core::fmt::Debug { + fn read_at(&self, address: usize, buf: &mut [u8]) -> Result; +} + +pub trait MappedModuleMemoryReader: core::fmt::Debug { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result; + fn len(&self) -> Result; + fn is_empty(&self) -> Result; +} + #[derive(Debug, serde::Deserialize, serde::Serialize)] pub enum ProcessReaderKind { Unspecified, @@ -21,3 +88,11 @@ pub enum ProcessReaderKind { File, Ptrace, } + +#[derive(Debug, thiserror::Error, serde::Deserialize, serde::Serialize)] +pub enum Error { + #[error("there was a failure making a direct syscall")] + Local(#[source] local::Error), + #[error("buffer was too small to contain the result")] + BufferTooSmall, +} diff --git a/crates/linux/process-backend/src/local/mod.rs b/crates/linux/process-backend/src/local/mod.rs index 77086186..4dbcf076 100644 --- a/crates/linux/process-backend/src/local/mod.rs +++ b/crates/linux/process-backend/src/local/mod.rs @@ -318,6 +318,91 @@ impl Backend { } } +impl crate::Backend for Backend { + type ProcessReader<'a> = ProcessReader<'a>; + + type FileReader<'a> = FileReader; + + type DirReader<'a> = DirReader; + + type MappedModuleMemoryReader<'a> = MappedModuleMemoryReader; + + fn pid(&self) -> crate::Result { + Ok(Backend::pid(self)) + } + + fn process_reader<'a>(&'a self) -> Self::ProcessReader<'a> { + Backend::process_reader(self) + } + + fn stop_process(&self) -> crate::Result<()> { + Backend::stop_process(self).map_err(crate::Error::Local) + } + + fn continue_process(&self) -> crate::Result<()> { + Backend::continue_process(self).map_err(crate::Error::Local) + } + + fn suspend_thread(&self, tid: libc::pid_t) -> crate::Result<()> { + Backend::suspend_thread(self, tid).map_err(crate::Error::Local) + } + + fn resume_thread(&self, tid: libc::pid_t) -> crate::Result<()> { + Backend::resume_thread(self, tid).map_err(crate::Error::Local) + } + + fn map_module_into_memory<'a>( + &'a self, + path: &CStr, + offset: u64, + ) -> crate::Result> { + Backend::map_module_into_memory(self, path, offset).map_err(crate::Error::Local) + } + + fn stat_file(&self, path: &CStr) -> crate::Result { + Backend::stat_file(self, path).map_err(crate::Error::Local) + } + + fn read_file<'a>(&'a self, path: &CStr) -> crate::Result> { + Backend::read_file(self, path).map_err(crate::Error::Local) + } + + fn read_dir<'a>(&'a self, path: &CStr) -> crate::Result> { + Backend::read_dir(self, path).map_err(crate::Error::Local) + } + + fn read_link(&self, path: &CStr, buf: &mut [u8]) -> crate::Result { + Backend::read_link(self, path, buf).map_err(crate::Error::Local) + } + + fn get_gen_regs(&self, tid: libc::pid_t) -> crate::Result { + Backend::get_gen_regs(self, tid).map_err(crate::Error::Local) + } + + fn get_fp_regs(&self, tid: libc::pid_t) -> crate::Result { + Backend::get_fp_regs(self, tid).map_err(crate::Error::Local) + } + + #[cfg(target_arch = "x86")] + fn get_fpx_regs(&self, tid: libc::pid_t) -> crate::Result { + Backend::get_fpx_regs(self, tid).map_err(crate::Error::Local) + } + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + fn ptrace_peekuser(&self, addr: usize) -> crate::Result<[u8; crate::PTRACE_DATA_LEN]> { + Backend::ptrace_peekuser(self, addr).map_err(crate::Error::Local) + } + + fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> crate::Result<()> { + Backend::force_process_reader_kind(self, kind).map_err(crate::Error::Local) + } + + #[cfg(feature = "testing")] + fn fail_one_syscall_with(&self, errno: c_int) { + Backend::fail_one_syscall_with(self, errno) + } +} + #[derive(Debug)] pub struct FileReader(OwnedFd); @@ -345,6 +430,12 @@ impl FileReader { } } +impl crate::FileReader for FileReader { + fn read(&mut self, buf: &mut [u8]) -> crate::Result { + FileReader::read(self, buf).map_err(crate::Error::Local) + } +} + #[derive(Debug)] pub struct DirReader { dirp: *mut libc::DIR, @@ -394,6 +485,19 @@ impl Drop for DirReader { } } +impl crate::DirReader for DirReader { + fn read_next_name(&mut self, buf: &mut [u8]) -> crate::Result { + let Some(name) = DirReader::read_next_name(self).map_err(crate::Error::Local)? else { + return Ok(0); + }; + let buf = buf + .get_mut(0..name.len()) + .ok_or(crate::Error::BufferTooSmall)?; + buf.copy_from_slice(name); + Ok(name.len()) + } +} + #[derive(Debug)] pub struct ProcessReader<'a>(&'a process_reader::ProcessReader); @@ -403,6 +507,12 @@ impl<'a> ProcessReader<'a> { } } +impl<'a> crate::ProcessReader for ProcessReader<'a> { + fn read_at(&self, address: usize, buf: &mut [u8]) -> crate::Result { + ProcessReader::read_at(self, address, buf).map_err(crate::Error::Local) + } +} + /// This is just a typesafe wrapper around ptrace(), which is vararg... But this is Rust, and /// playing loosey-goosey with types is really more of a C thing ;) unsafe fn ptrace( diff --git a/crates/linux/process-backend/src/local/module_reader.rs b/crates/linux/process-backend/src/local/module_reader.rs index e3c5e2b4..71c032ef 100644 --- a/crates/linux/process-backend/src/local/module_reader.rs +++ b/crates/linux/process-backend/src/local/module_reader.rs @@ -137,6 +137,23 @@ impl MappedModuleMemoryReader { } } +impl crate::MappedModuleMemoryReader for MappedModuleMemoryReader { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> core::result::Result { + let bytes = MappedModuleMemoryReader::read_at(self, offset, buf.len()) + .map_err(crate::Error::Local)?; + buf[0..bytes.len()].copy_from_slice(bytes); + Ok(bytes.len()) + } + + fn len(&self) -> core::result::Result { + Ok(MappedModuleMemoryReader::len(self)) + } + + fn is_empty(&self) -> core::result::Result { + Ok(MappedModuleMemoryReader::is_empty(self)) + } +} + #[derive(Debug)] struct Mapped { ptr: *mut c_void, diff --git a/src/linux/process_inspection/mod.rs b/src/linux/process_inspection/mod.rs index 9558c10f..82a51676 100644 --- a/src/linux/process_inspection/mod.rs +++ b/src/linux/process_inspection/mod.rs @@ -1,7 +1,7 @@ use super as linux; use crate::module_reader::{ModuleMemoryReadError, ReadError, ReadModuleMemory}; use linux::maps_reader; -use process_backend::{local, regs::*}; +use process_backend::{ProcessReader as _, local, regs::*}; use process_reader::{CopyFromProcessError, ProcessReader, ProcessReaderBackend}; use std::{ borrow::Cow, @@ -22,8 +22,9 @@ pub mod process_reader; pub(crate) type Result = core::result::Result; -// This is an arbitrary choice and may need to be tweaked +// These are both arbitrary choices and may need to be tweaked const MAX_PATH_LEN: usize = 65536; +const MAX_DIRECTORY_NAME_LENGTH: usize = 256; pub(crate) fn local(pid: libc::pid_t) -> Box { set_process_backend_drop_fail_handler(); @@ -37,14 +38,14 @@ pub trait ProcessInspector: core::fmt::Debug { fn continue_process(&self) -> Result<()>; fn suspend_thread(&self, tid: libc::pid_t) -> Result<()>; fn resume_thread(&self, tid: libc::pid_t) -> Result<()>; - fn map_module_into_memory( - &self, + fn map_module_into_memory<'a>( + &'a self, path: PathBuf, offset: u64, - ) -> Result; + ) -> Result>; fn stat_file(&self, path: PathBuf) -> Result; - fn read_file(&self, path: PathBuf) -> Result; - fn read_dir(&self, path: PathBuf) -> Result; + fn read_file<'a>(&'a self, path: PathBuf) -> Result>; + fn read_dir<'a>(&'a self, path: PathBuf) -> Result>; fn read_link(&self, path: PathBuf) -> Result; fn get_gen_regs(&self, tid: libc::pid_t) -> Result; fn get_fp_regs(&self, tid: libc::pid_t) -> Result; @@ -60,95 +61,94 @@ pub trait ProcessInspector: core::fmt::Debug { fn fail_one_syscall_with(&self, errno: core::ffi::c_int); } -impl ProcessInspector for local::Backend { +impl ProcessInspector for B { fn process_reader<'a>(&'a self) -> ProcessReader<'a> { ProcessReader::new(self) } fn pid(&self) -> Result { - Ok(local::Backend::pid(self)) + B::pid(self).map_err(Error::Backend) } fn stop_process(&self) -> Result<()> { - local::Backend::stop_process(self).map_err(Error::Local) + B::stop_process(self).map_err(Error::Backend) } fn continue_process(&self) -> Result<()> { - local::Backend::continue_process(self).map_err(Error::Local) + B::continue_process(self).map_err(Error::Backend) } fn suspend_thread(&self, tid: libc::pid_t) -> Result<()> { - local::Backend::suspend_thread(self, tid).map_err(Error::Local) + B::suspend_thread(self, tid).map_err(Error::Backend) } fn resume_thread(&self, tid: libc::pid_t) -> Result<()> { - local::Backend::resume_thread(self, tid).map_err(Error::Local) + B::resume_thread(self, tid).map_err(Error::Backend) } - fn map_module_into_memory( - &self, + fn map_module_into_memory<'a>( + &'a self, path: PathBuf, offset: u64, - ) -> Result { + ) -> Result> { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - let reader = - local::Backend::map_module_into_memory(self, &c_path, offset).map_err(Error::Local)?; - Ok(MappedModuleMemoryReader(reader)) + let reader = B::map_module_into_memory(self, &c_path, offset).map_err(Error::Backend)?; + Ok(MappedModuleMemoryReader(Box::new(reader))) } fn stat_file(&self, path: PathBuf) -> Result { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - local::Backend::stat_file(self, &c_path).map_err(Error::Local) + B::stat_file(self, &c_path).map_err(Error::Backend) } - fn read_file(&self, path: PathBuf) -> Result { + fn read_file<'a>(&'a self, path: PathBuf) -> Result> { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - let reader = local::Backend::read_file(self, &c_path).map_err(Error::Local)?; - Ok(FileReader(reader)) + let reader = B::read_file(self, &c_path).map_err(Error::Backend)?; + Ok(FileReader(Box::new(reader))) } - fn read_dir(&self, path: PathBuf) -> Result { + fn read_dir<'a>(&'a self, path: PathBuf) -> Result> { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - let reader = local::Backend::read_dir(self, &c_path).map_err(Error::Local)?; - Ok(DirReader(reader)) + let reader = B::read_dir(self, &c_path).map_err(Error::Backend)?; + Ok(DirReader(Box::new(reader))) } fn read_link(&self, path: PathBuf) -> Result { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); let mut buf = vec![0u8; MAX_PATH_LEN]; - let len = local::Backend::read_link(self, &c_path, &mut buf).map_err(Error::Local)?; + let len = B::read_link(self, &c_path, &mut buf).map_err(Error::Backend)?; buf.truncate(len); Ok(PathBuf::from(OsString::from_vec(buf))) } fn get_gen_regs(&self, tid: libc::pid_t) -> Result { - local::Backend::get_gen_regs(self, tid).map_err(Error::Local) + B::get_gen_regs(self, tid).map_err(Error::Backend) } fn get_fp_regs(&self, tid: libc::pid_t) -> Result { - local::Backend::get_fp_regs(self, tid).map_err(Error::Local) + B::get_fp_regs(self, tid).map_err(Error::Backend) } #[cfg(target_arch = "x86")] fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { - local::Backend::get_fpx_regs(self, tid).map_err(Error::Local) + B::get_fpx_regs(self, tid).map_err(Error::Backend) } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] fn ptrace_peekuser(&self, addr: usize) -> Result<[u8; PTRACE_DATA_LEN]> { - local::Backend::ptrace_peekuser(self, addr).map_err(Error::Local) + B::ptrace_peekuser(self, addr).map_err(Error::Backend) } fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()> { - local::Backend::force_process_reader_kind(self, kind).map_err(Error::Local) + B::force_process_reader_kind(self, kind).map_err(Error::Backend) } #[doc(hidden)] fn fail_one_syscall_with(&self, errno: c_int) { - local::Backend::fail_one_syscall_with(self, errno) + B::fail_one_syscall_with(self, errno) } } -impl ProcessReaderBackend for local::Backend { +impl ProcessReaderBackend for B { fn process_inspector(&self) -> &dyn ProcessInspector { self } @@ -158,72 +158,68 @@ impl ProcessReaderBackend for local::Backend { src: usize, dst: &mut [u8], ) -> core::result::Result { - local::Backend::process_reader(self) + B::process_reader(self) .read_at(src, dst) - .map_err(|e| CopyFromProcessError::Backend(Error::Local(e))) + .map_err(|e| CopyFromProcessError::Backend(Error::Backend(e))) } } #[derive(Debug)] -pub struct FileReader(local::FileReader); +pub struct FileReader<'a>(Box); -impl io::Read for FileReader { +impl<'a> io::Read for FileReader<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result { self.0 .read(buf) - .map_err(Error::Local) + .map_err(Error::Backend) .map_err(io::Error::other) } } #[derive(Debug)] -pub struct DirReader(local::DirReader); +pub struct DirReader<'a>(Box); -impl Iterator for DirReader { +impl<'a> Iterator for DirReader<'a> { type Item = Result; fn next(&mut self) -> Option { - Some( - self.0 - .read_next_name() - .transpose()? - .map(<[u8]>::to_vec) - .map(OsString::from_vec) - .map_err(Error::Local), - ) + let mut buf = vec![0; MAX_DIRECTORY_NAME_LENGTH]; + match self.0.read_next_name(&mut buf).map_err(Error::Backend) { + Ok(0) => None, + Ok(len) => { + buf.truncate(len); + Some(Ok(OsString::from_vec(buf))) + } + Err(e) => Some(Err(e)), + } } } #[derive(Debug)] -pub struct MappedModuleMemoryReader(local::MappedModuleMemoryReader); +pub struct MappedModuleMemoryReader<'a>(Box); -impl MappedModuleMemoryReader { +impl<'a> MappedModuleMemoryReader<'a> { pub fn read_exact_at(&self, mut offset: usize, mut buf: &mut [u8]) -> Result<()> { if buf.is_empty() { return Ok(()); } loop { - let bytes = self.0.read_at(offset, buf.len()).map_err(Error::Local)?; - if bytes.is_empty() { - return Err(Error::UnexpectedEndOfBuffer); - } - let (dst, tail) = buf.split_at_mut(bytes.len()); - dst.copy_from_slice(bytes); - if tail.is_empty() { + let bytes_read = self.0.read_at(offset, buf).map_err(Error::Backend)?; + if bytes_read == buf.len() { return Ok(()); } offset = offset - .checked_add(dst.len()) + .checked_add(bytes_read) .ok_or(Error::AddressOverflowed)?; - buf = tail; + buf = &mut buf[bytes_read..]; } } pub fn len(&self) -> Result { - Ok(self.0.len()) + self.0.len().map_err(Error::Backend) } } -impl ReadModuleMemory for MappedModuleMemoryReader { +impl<'a> ReadModuleMemory for MappedModuleMemoryReader<'a> { fn read( &self, offset: u64, @@ -266,8 +262,8 @@ impl ReadModuleMemory for MappedModuleMemoryReader { #[derive(Debug, thiserror::Error, serde::Serialize)] pub enum Error { - #[error("an error occurred running a syscall directly")] - Local(#[source] local::Error), + #[error("an error occurred running the backend")] + Backend(#[source] process_backend::Error), #[error("an address overflowed")] AddressOverflowed, #[error("unexpected end of buffer reached")]