From 76173b06d68e187df053df66d387382bb0e9024d Mon Sep 17 00:00:00 2001 From: Chris Martin Date: Fri, 7 Aug 2026 14:23:32 -0400 Subject: [PATCH] Isolated Processes 13: Change ProcessInspector into a dyn trait --- src/bin/test.rs | 4 +- src/linux/android.rs | 8 +- src/linux/auxv/mod.rs | 2 +- src/linux/dso_debug.rs | 2 +- src/linux/dumper_cpu_info/arm.rs | 2 +- src/linux/dumper_cpu_info/x86.rs | 2 +- src/linux/maps_reader.rs | 8 +- src/linux/minidump_writer/app_memory.rs | 2 +- .../minidump_writer/handle_data_stream.rs | 6 +- src/linux/minidump_writer/mappings.rs | 8 +- src/linux/minidump_writer/mod.rs | 68 +++-- .../minidump_writer/systeminfo_stream.rs | 2 +- .../minidump_writer/thread_list_stream.rs | 4 +- src/linux/module_reader.rs | 4 +- src/linux/process_inspection/mod.rs | 253 ++++++++---------- .../process_inspection/process_reader.rs | 74 ++--- src/linux/thread_info/aarch64.rs | 5 +- src/linux/thread_info/arm.rs | 5 +- src/linux/thread_info/mod.rs | 2 +- src/linux/thread_info/x86.rs | 5 +- 20 files changed, 232 insertions(+), 234 deletions(-) diff --git a/src/bin/test.rs b/src/bin/test.rs index c5af4870..f1532585 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -133,7 +133,7 @@ mod linux { .unwrap(); let stack_res = MinidumpWriter::copy_from_process( - &dumper.process_inspector, + dumper.process_inspector.as_ref(), stack_var, std::mem::size_of::(), )?; @@ -141,7 +141,7 @@ mod linux { test!(stack_res == expected_stack, "stack var not correct"); let heap_res = MinidumpWriter::copy_from_process( - &dumper.process_inspector, + dumper.process_inspector.as_ref(), heap_var, std::mem::size_of::(), )?; diff --git a/src/linux/android.rs b/src/linux/android.rs index 2d9e70d5..bef75da7 100644 --- a/src/linux/android.rs +++ b/src/linux/android.rs @@ -49,7 +49,7 @@ struct DynVaddresses { } fn has_android_packed_relocations( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, load_bias: usize, vaddrs: DynVaddresses, ) -> Result<()> { @@ -71,7 +71,7 @@ fn has_android_packed_relocations( } fn get_effective_load_bias( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, ehdr: &elf_header::Header, address: usize, ) -> usize { @@ -91,7 +91,7 @@ fn get_effective_load_bias( } fn parse_loaded_elf_program_headers( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, ehdr: &elf_header::Header, address: usize, ) -> DynVaddresses { @@ -131,7 +131,7 @@ fn parse_loaded_elf_program_headers( } pub fn late_process_mappings( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, mappings: &mut [MappingInfo], ) -> Result<()> { // Only consider exec mappings that indicate a file path was mapped, and diff --git a/src/linux/auxv/mod.rs b/src/linux/auxv/mod.rs index 7c804dcd..a9d5d070 100644 --- a/src/linux/auxv/mod.rs +++ b/src/linux/auxv/mod.rs @@ -87,7 +87,7 @@ pub struct AuxvDumpInfo { impl AuxvDumpInfo { pub fn try_filling_missing_info( &mut self, - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, pid: Pid, mut soft_errors: impl WriteErrorList, ) -> Result<(), AuxvError> { diff --git a/src/linux/dso_debug.rs b/src/linux/dso_debug.rs index 011f61cb..6816bab4 100644 --- a/src/linux/dso_debug.rs +++ b/src/linux/dso_debug.rs @@ -97,7 +97,7 @@ pub struct RDebug { } pub fn write_dso_debug_stream( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, buffer: &mut Buffer, auxv: &AuxvDumpInfo, ) -> Result { diff --git a/src/linux/dumper_cpu_info/arm.rs b/src/linux/dumper_cpu_info/arm.rs index 1a331d4b..42603bb9 100644 --- a/src/linux/dumper_cpu_info/arm.rs +++ b/src/linux/dumper_cpu_info/arm.rs @@ -136,7 +136,7 @@ fn parse_features(_val: &str) -> u32 { } pub fn write_cpu_information( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, sys_info: &mut MDRawSystemInfo, ) -> Result<()> { // The CPUID value is broken up in several entries in /proc/cpuinfo. diff --git a/src/linux/dumper_cpu_info/x86.rs b/src/linux/dumper_cpu_info/x86.rs index b1e8ddcc..27843e7c 100644 --- a/src/linux/dumper_cpu_info/x86.rs +++ b/src/linux/dumper_cpu_info/x86.rs @@ -24,7 +24,7 @@ impl CpuInfoEntry { } pub fn write_cpu_information( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, sys_info: &mut MDRawSystemInfo, ) -> Result<()> { let vendor_id_name = "vendor_id"; diff --git a/src/linux/maps_reader.rs b/src/linux/maps_reader.rs index 0a19072a..9e8ca80d 100644 --- a/src/linux/maps_reader.rs +++ b/src/linux/maps_reader.rs @@ -136,7 +136,7 @@ fn sanitize_path(pathname: OsString) -> OsString { impl MappingInfo { /// Get the mappings for the given process. pub fn for_pid( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, pid: i32, linux_gate_loc: Option, ) -> Result> { @@ -298,7 +298,7 @@ impl MappingInfo { /// Find the shared object name (SONAME) by examining the ELF information /// for the mapping. - fn so_name(&self, process_inspector: &ProcessInspector) -> Result { + fn so_name(&self, process_inspector: &dyn ProcessInspector) -> Result { let path = Path::new(self.name.as_deref().unwrap_or_default()); super::module_reader::read_soname_from_file(process_inspector, path, self.offset) .map_err(MapsReaderError::ReadSoNameFromFileFailed) @@ -311,7 +311,7 @@ impl MappingInfo { pub fn get_mapping_effective_path_name_and_version( &self, - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, soname: Option, ) -> Result<(PathBuf, String, Option)> { let mut file_path = PathBuf::from(self.name.clone().unwrap_or_default()); @@ -731,7 +731,7 @@ a4840000-a4873000 rw-p 09021000 08:12 393449 /data/app/org.mozilla.firefox-1 let process_inspector = process_inspection::local(0); let (file_path, file_name, _version) = mappings[0] - .get_mapping_effective_path_name_and_version(&process_inspector, None) + .get_mapping_effective_path_name_and_version(process_inspector.as_ref(), None) .expect("Couldn't get effective name for mapping"); assert_eq!(file_name, "libmozgtk.so"); assert_eq!( diff --git a/src/linux/minidump_writer/app_memory.rs b/src/linux/minidump_writer/app_memory.rs index a2f9ba04..163e710a 100644 --- a/src/linux/minidump_writer/app_memory.rs +++ b/src/linux/minidump_writer/app_memory.rs @@ -5,7 +5,7 @@ impl MinidumpWriter { pub fn write_app_memory(&mut self, buffer: &mut DumpBuf) -> Result<(), CopyFromProcessError> { for app_memory in &self.app_memory { let data_copy = Self::copy_from_process( - &self.process_inspector, + self.process_inspector.as_ref(), app_memory.ptr, app_memory.length, )?; diff --git a/src/linux/minidump_writer/handle_data_stream.rs b/src/linux/minidump_writer/handle_data_stream.rs index 0e1db981..6ab4dfe5 100644 --- a/src/linux/minidump_writer/handle_data_stream.rs +++ b/src/linux/minidump_writer/handle_data_stream.rs @@ -1,6 +1,6 @@ use { super::*, - crate::{linux::process_inspection, mem_writer::MemoryWriter}, + crate::{linux::process_inspection::ProcessInspector, mem_writer::MemoryWriter}, std::{ ffi::OsStr, mem, @@ -9,7 +9,7 @@ use { }; fn descriptor_from_path( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, buffer: &mut DumpBuf, path: &Path, ) -> Option { @@ -73,7 +73,7 @@ impl MinidumpWriter { .filter_map(|filename| filename.ok()) .filter_map(|filename| { let path = proc_fd_path.join(filename); - descriptor_from_path(&self.process_inspector, buffer, &path) + descriptor_from_path(self.process_inspector.as_ref(), buffer, &path) }) .collect(); let number_of_descriptors = descriptors.len() as u32; diff --git a/src/linux/minidump_writer/mappings.rs b/src/linux/minidump_writer/mappings.rs index 027a5507..8b93ffd0 100644 --- a/src/linux/minidump_writer/mappings.rs +++ b/src/linux/minidump_writer/mappings.rs @@ -44,7 +44,7 @@ impl MinidumpWriter { log::debug!("failed to get build id from process memory ({e}), attempting to retrieve from {}", path.display()); - module_reader::read_build_id_from_file(&self.process_inspector, path.as_ref()).map_err(errors::WriterError::ModuleReaderError) + module_reader::read_build_id_from_file(self.process_inspector.as_ref(), path.as_ref()).map_err(errors::WriterError::ModuleReaderError) }) .unwrap_or_else(|e| { log::warn!("failed to get build id for mapping: {e}"); @@ -61,7 +61,7 @@ impl MinidumpWriter { let soname = self.soname_from_process_memory_for_index(map_idx).ok(); let module = fill_raw_module( - &self.process_inspector, + self.process_inspector.as_ref(), buffer, &self.mappings[map_idx], &identifier, @@ -74,7 +74,7 @@ impl MinidumpWriter { for user in &self.user_mapping_list { // GUID was provided by caller. let module = fill_raw_module( - &self.process_inspector, + self.process_inspector.as_ref(), buffer, &user.mapping, &user.identifier, @@ -99,7 +99,7 @@ impl MinidumpWriter { } } fn fill_raw_module( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, buffer: &mut DumpBuf, mapping: &MappingInfo, identifier: &[u8], diff --git a/src/linux/minidump_writer/mod.rs b/src/linux/minidump_writer/mod.rs index 2afd1fcc..f84fcb16 100644 --- a/src/linux/minidump_writer/mod.rs +++ b/src/linux/minidump_writer/mod.rs @@ -76,7 +76,7 @@ pub struct MinidumpWriterConfig { crashing_thread_context: CrashingThreadContext, stop_timeout: Duration, direct_auxv_dump_info: Option, - process_inspector: ProcessInspector, + process_inspector: Box, } #[derive(Debug)] @@ -99,7 +99,7 @@ pub struct MinidumpWriter { pub crash_context: Option, pub app_memory: AppMemoryList, pub memory_blocks: Vec, - pub process_inspector: ProcessInspector, + pub process_inspector: Box, } #[derive(Debug, Clone)] @@ -194,6 +194,7 @@ impl MinidumpWriterConfig { self.direct_auxv_dump_info = Some(direct_auxv_dump_info); self } + /// Generates a minidump and writes to the destination provided. Returns the in-memory /// version of the minidump as well. pub fn write(self, destination: &mut (impl Write + Seek)) -> Result, WriterError> { @@ -260,7 +261,7 @@ impl MinidumpWriter { // Even if we completely fail to fill in any additional Auxv info, we can still press // forward. if let Err(e) = self.auxv.try_filling_missing_info( - &self.process_inspector, + self.process_inspector.as_ref(), self.process_id, soft_errors.subwriter(InitError::FillMissingAuxvInfoErrors), ) { @@ -296,7 +297,7 @@ impl MinidumpWriter { #[cfg(target_os = "android")] { - late_process_mappings(&self.process_inspector, &mut self.mappings)?; + late_process_mappings(self.process_inspector.as_ref(), &mut self.mappings)?; } if self.skip_stacks_if_mapping_unreferenced { @@ -363,7 +364,7 @@ impl MinidumpWriter { dir_section.write_to_file(buffer, Some(dirent))?; let dirent = systeminfo_stream::write( - &self.process_inspector, + self.process_inspector.as_ref(), buffer, soft_errors.subwriter(WriterError::WriteSystemInfoErrors), )?; @@ -397,14 +398,14 @@ impl MinidumpWriter { let trunc = proc_root.len(); proc_root.push_str($fname); - file_entry!(res write_file(&self.process_inspector, buffer, &proc_root), $kind, $err); + file_entry!(res write_file(self.process_inspector.as_ref(), buffer, &proc_root), $kind, $err); proc_root.truncate(trunc); }; } file_entry!( - res write_file(&self.process_inspector, buffer, "/proc/cpuinfo"), + res write_file(self.process_inspector.as_ref(), buffer, "/proc/cpuinfo"), LinuxCpuInfo, WriteCpuInfoFailed ); @@ -415,8 +416,8 @@ impl MinidumpWriter { #[cfg(not(target_os = "android"))] { file_entry!( - res write_file(&self.process_inspector, buffer, "/etc/lsb-release") - .or_else(|_| write_file(&self.process_inspector, buffer, "/etc/os-release")), + res write_file(self.process_inspector.as_ref(), buffer, "/etc/lsb-release") + .or_else(|_| write_file(self.process_inspector.as_ref(), buffer, "/etc/os-release")), LinuxLsbRelease, WriteOsReleaseInfoFailed ); @@ -427,14 +428,17 @@ impl MinidumpWriter { file_entry!("auxv", LinuxAuxv, WriteEnvironmentFailed); file_entry!("maps", LinuxMaps, WriteMapsFailed); - let dirent = - match dso_debug::write_dso_debug_stream(&self.process_inspector, buffer, &self.auxv) { - Ok(dirent) => dirent, - Err(e) => { - soft_errors.push(WriterError::WriteDSODebugStreamFailed(e)); - Default::default() - } - }; + let dirent = match dso_debug::write_dso_debug_stream( + self.process_inspector.as_ref(), + buffer, + &self.auxv, + ) { + Ok(dirent) => dirent, + Err(e) => { + soft_errors.push(WriterError::WriteDSODebugStreamFailed(e)); + Default::default() + } + }; dir_section.write_to_file(buffer, Some(dirent))?; file_entry!("limits", MozLinuxLimits, WriteLimitsFailed); @@ -501,7 +505,7 @@ impl MinidumpWriter { }; let stack_copy = match MinidumpWriter::copy_from_process( - &self.process_inspector, + self.process_inspector.as_ref(), valid_stack_pointer, stack_len, ) { @@ -519,7 +523,10 @@ impl MinidumpWriter { } /// Suspends a thread by attaching to it. - fn suspend_thread(process_inspector: &ProcessInspector, tid: Pid) -> Result<(), WriterError> { + fn suspend_thread( + process_inspector: &dyn ProcessInspector, + tid: Pid, + ) -> Result<(), WriterError> { process_inspector .suspend_thread(tid) .map_err(WriterError::SuspendThreadFailed)?; @@ -557,7 +564,10 @@ impl MinidumpWriter { } /// Resumes a thread by detaching from it. - fn resume_thread(process_inspector: &ProcessInspector, tid: Pid) -> Result<(), WriterError> { + fn resume_thread( + process_inspector: &dyn ProcessInspector, + tid: Pid, + ) -> Result<(), WriterError> { process_inspector .resume_thread(tid) .map_err(WriterError::ResumeThreadFailed) @@ -568,15 +578,15 @@ impl MinidumpWriter { // If the thread either disappeared before we could attach to it, or if // it was part of the seccomp sandbox's trusted code, it is OK to // silently drop it from the minidump. - self.threads.retain( - |x| match Self::suspend_thread(&self.process_inspector, x.tid) { + self.threads.retain(|x| { + match Self::suspend_thread(self.process_inspector.as_ref(), x.tid) { Ok(()) => true, Err(e) => { soft_errors.push(e); false } - }, - ); + } + }); self.threads_suspended = true; @@ -586,7 +596,7 @@ impl MinidumpWriter { fn resume_threads(&mut self, mut soft_errors: impl WriteErrorList) { if self.threads_suspended { for thread in &self.threads { - match Self::resume_thread(&self.process_inspector, thread.tid) { + match Self::resume_thread(self.process_inspector.as_ref(), thread.tid) { Ok(()) => (), Err(e) => { soft_errors.push(e); @@ -707,7 +717,7 @@ impl MinidumpWriter { // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more // information. self.mappings = MappingInfo::for_pid( - &self.process_inspector, + self.process_inspector.as_ref(), self.process_id, self.auxv.get_linux_gate_address(), ) @@ -745,7 +755,7 @@ impl MinidumpWriter { return Err(ThreadInfoError::IndexOutOfBounds(index, self.threads.len())); } - ThreadInfo::create(&self.process_inspector, self.threads[index].tid) + ThreadInfo::create(self.process_inspector.as_ref(), self.threads[index].tid) } // Returns a valid stack pointer and the mapping that contains the stack. @@ -950,7 +960,7 @@ impl MinidumpWriter { /// allocated copy #[inline] pub fn copy_from_process( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, src: usize, length: usize, ) -> Result, CopyFromProcessError> { @@ -971,7 +981,7 @@ impl Drop for MinidumpWriter { } fn write_file( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, buffer: &mut DumpBuf, filename: &str, ) -> std::result::Result { diff --git a/src/linux/minidump_writer/systeminfo_stream.rs b/src/linux/minidump_writer/systeminfo_stream.rs index 52cbc768..6d435fc7 100644 --- a/src/linux/minidump_writer/systeminfo_stream.rs +++ b/src/linux/minidump_writer/systeminfo_stream.rs @@ -14,7 +14,7 @@ pub enum SectionSystemInfoError { } pub fn write( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, buffer: &mut DumpBuf, mut soft_errors: impl WriteErrorList, ) -> Result { diff --git a/src/linux/minidump_writer/thread_list_stream.rs b/src/linux/minidump_writer/thread_list_stream.rs index f0160af9..e0febe10 100644 --- a/src/linux/minidump_writer/thread_list_stream.rs +++ b/src/linux/minidump_writer/thread_list_stream.rs @@ -137,7 +137,7 @@ impl MinidumpWriter { (end_of_range - ip_memory_d.start_of_memory_range) as u32; let memory_copy = MinidumpWriter::copy_from_process( - &self.process_inspector, + self.process_inspector.as_ref(), ip_memory_d.start_of_memory_range as _, ip_memory_d.memory.data_size as usize, ) @@ -212,7 +212,7 @@ impl MinidumpWriter { }; let mut stack_bytes = MinidumpWriter::copy_from_process( - &self.process_inspector, + self.process_inspector.as_ref(), valid_stack_ptr, stack_len, ) diff --git a/src/linux/module_reader.rs b/src/linux/module_reader.rs index a42f95c8..e6450fe7 100644 --- a/src/linux/module_reader.rs +++ b/src/linux/module_reader.rs @@ -160,7 +160,7 @@ fn section_header_with_name<'sc, MM: ReadModuleMemory>( } pub fn read_build_id_from_file( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, path: &Path, ) -> Result, Error> { let module_memory_reader = process_inspector @@ -191,7 +191,7 @@ pub fn read_build_id_from_module(module_memory: impl ReadModuleMemory) -> Result } pub fn read_soname_from_file( - process_inspector: &ProcessInspector, + process_inspector: &dyn ProcessInspector, path: &Path, offset: usize, ) -> Result { diff --git a/src/linux/process_inspection/mod.rs b/src/linux/process_inspection/mod.rs index 9a328153..9558c10f 100644 --- a/src/linux/process_inspection/mod.rs +++ b/src/linux/process_inspection/mod.rs @@ -2,7 +2,7 @@ use super as linux; use crate::module_reader::{ModuleMemoryReadError, ReadError, ReadModuleMemory}; use linux::maps_reader; use process_backend::{local, regs::*}; -use process_reader::ProcessReader; +use process_reader::{CopyFromProcessError, ProcessReader, ProcessReaderBackend}; use std::{ borrow::Cow, ffi::{CString, OsString, c_int}, @@ -25,199 +25,176 @@ pub(crate) type Result = core::result::Result; // This is an arbitrary choice and may need to be tweaked const MAX_PATH_LEN: usize = 65536; -#[derive(Debug)] -pub struct ProcessInspector { - pid: libc::pid_t, - backend: Backend, +pub(crate) fn local(pid: libc::pid_t) -> Box { + set_process_backend_drop_fail_handler(); + Box::new(local::Backend::new(pid)) } -#[derive(Debug)] -enum Backend { - Local { backend: local::Backend }, -} +pub trait ProcessInspector: core::fmt::Debug { + fn process_reader<'a>(&'a self) -> ProcessReader<'a>; + fn pid(&self) -> Result; + 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( + &self, + path: PathBuf, + offset: u64, + ) -> Result; + fn stat_file(&self, path: PathBuf) -> Result; + fn read_file(&self, path: PathBuf) -> Result; + fn read_dir(&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; -pub(crate) fn local(pid: libc::pid_t) -> ProcessInspector { - set_process_backend_drop_fail_handler(); + #[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]>; - let backend = local::Backend::new(pid); + fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()>; - ProcessInspector { - pid, - backend: Backend::Local { backend }, - } + fn fail_one_syscall_with(&self, errno: core::ffi::c_int); } -impl ProcessInspector { - pub fn process_reader(&self) -> ProcessReader<'_> { +impl ProcessInspector for local::Backend { + fn process_reader<'a>(&'a self) -> ProcessReader<'a> { ProcessReader::new(self) } - pub fn pid(&self) -> Result { - match &self.backend { - Backend::Local { backend, .. } => Ok(backend.pid()), - } + fn pid(&self) -> Result { + Ok(local::Backend::pid(self)) } - pub fn stop_process(&self) -> Result<()> { - match &self.backend { - Backend::Local { backend, .. } => backend.stop_process().map_err(Error::Local), - } + fn stop_process(&self) -> Result<()> { + local::Backend::stop_process(self).map_err(Error::Local) } - pub fn continue_process(&self) -> Result<()> { - match &self.backend { - Backend::Local { backend, .. } => backend.continue_process().map_err(Error::Local), - } + fn continue_process(&self) -> Result<()> { + local::Backend::continue_process(self).map_err(Error::Local) } - pub fn suspend_thread(&self, tid: libc::pid_t) -> Result<()> { - match &self.backend { - Backend::Local { backend, .. } => backend.suspend_thread(tid).map_err(Error::Local), - } + fn suspend_thread(&self, tid: libc::pid_t) -> Result<()> { + local::Backend::suspend_thread(self, tid).map_err(Error::Local) } - pub fn resume_thread(&self, tid: libc::pid_t) -> Result<()> { - match &self.backend { - Backend::Local { backend, .. } => backend.resume_thread(tid).map_err(Error::Local), - } + fn resume_thread(&self, tid: libc::pid_t) -> Result<()> { + local::Backend::resume_thread(self, tid).map_err(Error::Local) } - pub fn map_module_into_memory( + fn map_module_into_memory( &self, path: PathBuf, offset: u64, ) -> Result { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - match &self.backend { - Backend::Local { backend, .. } => backend - .map_module_into_memory(&c_path, offset) - .map(MappedModuleMemoryReader::Local) - .map_err(Error::Local), - } + let reader = + local::Backend::map_module_into_memory(self, &c_path, offset).map_err(Error::Local)?; + Ok(MappedModuleMemoryReader(reader)) } - pub fn stat_file(&self, path: PathBuf) -> Result { + fn stat_file(&self, path: PathBuf) -> Result { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - match &self.backend { - Backend::Local { backend, .. } => backend.stat_file(&c_path).map_err(Error::Local), - } + local::Backend::stat_file(self, &c_path).map_err(Error::Local) } - pub fn read_file(&self, path: PathBuf) -> Result { + fn read_file(&self, path: PathBuf) -> Result { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - match &self.backend { - Backend::Local { backend, .. } => backend - .read_file(&c_path) - .map(FileReader::Local) - .map_err(Error::Local), - } + let reader = local::Backend::read_file(self, &c_path).map_err(Error::Local)?; + Ok(FileReader(reader)) } - pub fn read_dir(&self, path: PathBuf) -> Result { + fn read_dir(&self, path: PathBuf) -> Result { let c_path = CString::new(path.into_os_string().into_vec()).unwrap(); - match &self.backend { - Backend::Local { backend, .. } => backend - .read_dir(&c_path) - .map(DirReader::Local) - .map_err(Error::Local), - } + let reader = local::Backend::read_dir(self, &c_path).map_err(Error::Local)?; + Ok(DirReader(reader)) } - pub fn read_link(&self, path: PathBuf) -> Result { + 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 = match &self.backend { - Backend::Local { backend, .. } => { - backend.read_link(&c_path, &mut buf).map_err(Error::Local)? - } - }; - + let len = local::Backend::read_link(self, &c_path, &mut buf).map_err(Error::Local)?; buf.truncate(len); Ok(PathBuf::from(OsString::from_vec(buf))) } - - pub fn get_gen_regs(&self, tid: libc::pid_t) -> Result { - match &self.backend { - Backend::Local { backend, .. } => backend.get_gen_regs(tid).map_err(Error::Local), - } + fn get_gen_regs(&self, tid: libc::pid_t) -> Result { + local::Backend::get_gen_regs(self, tid).map_err(Error::Local) } - pub fn get_fp_regs(&self, tid: libc::pid_t) -> Result { - match &self.backend { - Backend::Local { backend, .. } => backend.get_fp_regs(tid).map_err(Error::Local), - } + fn get_fp_regs(&self, tid: libc::pid_t) -> Result { + local::Backend::get_fp_regs(self, tid).map_err(Error::Local) } #[cfg(target_arch = "x86")] - pub fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { - match &self.backend { - Backend::Local { backend, .. } => backend.get_fpx_regs(tid).map_err(Error::Local), - } + fn get_fpx_regs(&self, tid: libc::pid_t) -> Result { + local::Backend::get_fpx_regs(self, tid).map_err(Error::Local) } #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - pub fn ptrace_peekuser(&self, addr: usize) -> Result<[u8; PTRACE_DATA_LEN]> { - match &self.backend { - Backend::Local { backend, .. } => backend.ptrace_peekuser(addr).map_err(Error::Local), - } + fn ptrace_peekuser(&self, addr: usize) -> Result<[u8; PTRACE_DATA_LEN]> { + local::Backend::ptrace_peekuser(self, addr).map_err(Error::Local) } - pub fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()> { - match &mut self.backend { - Backend::Local { backend, .. } => backend - .force_process_reader_kind(kind) - .map_err(Error::Local), - } + fn force_process_reader_kind(&mut self, kind: ProcessReaderKind) -> Result<()> { + local::Backend::force_process_reader_kind(self, kind).map_err(Error::Local) } #[doc(hidden)] - pub fn fail_one_syscall_with(&self, errno: c_int) { - match &self.backend { - Backend::Local { backend, .. } => backend.fail_one_syscall_with(errno), - } + fn fail_one_syscall_with(&self, errno: c_int) { + local::Backend::fail_one_syscall_with(self, errno) } } -#[derive(Debug)] -pub enum FileReader { - Local(local::FileReader), +impl ProcessReaderBackend for local::Backend { + fn process_inspector(&self) -> &dyn ProcessInspector { + self + } + + fn read_at( + &self, + src: usize, + dst: &mut [u8], + ) -> core::result::Result { + local::Backend::process_reader(self) + .read_at(src, dst) + .map_err(|e| CopyFromProcessError::Backend(Error::Local(e))) + } } +#[derive(Debug)] +pub struct FileReader(local::FileReader); + impl io::Read for FileReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { - match self { - Self::Local(l) => l.read(buf).map_err(Error::Local), - } - .map_err(io::Error::other) + self.0 + .read(buf) + .map_err(Error::Local) + .map_err(io::Error::other) } } #[derive(Debug)] -pub enum DirReader { - Local(local::DirReader), -} +pub struct DirReader(local::DirReader); impl Iterator for DirReader { type Item = Result; fn next(&mut self) -> Option { - match self { - Self::Local(l) => Some( - l.read_next_name() - .transpose()? - .map(<[u8]>::to_vec) - .map(OsString::from_vec) - .map_err(Error::Local), - ), - } + Some( + self.0 + .read_next_name() + .transpose()? + .map(<[u8]>::to_vec) + .map(OsString::from_vec) + .map_err(Error::Local), + ) } } #[derive(Debug)] -pub enum MappedModuleMemoryReader { - Local(local::MappedModuleMemoryReader), -} +pub struct MappedModuleMemoryReader(local::MappedModuleMemoryReader); impl MappedModuleMemoryReader { pub fn read_exact_at(&self, mut offset: usize, mut buf: &mut [u8]) -> Result<()> { @@ -225,28 +202,24 @@ impl MappedModuleMemoryReader { return Ok(()); } - match self { - Self::Local(l) => loop { - let bytes = l.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() { - return Ok(()); - } - offset = offset - .checked_add(dst.len()) - .ok_or(Error::AddressOverflowed)?; - buf = tail; - }, + 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() { + return Ok(()); + } + offset = offset + .checked_add(dst.len()) + .ok_or(Error::AddressOverflowed)?; + buf = tail; } } pub fn len(&self) -> Result { - match self { - Self::Local(l) => Ok(l.len()), - } + Ok(self.0.len()) } } diff --git a/src/linux/process_inspection/process_reader.rs b/src/linux/process_inspection/process_reader.rs index ed467c7f..7e2ccd82 100644 --- a/src/linux/process_inspection/process_reader.rs +++ b/src/linux/process_inspection/process_reader.rs @@ -1,5 +1,5 @@ use super::{ - Backend, Error, ProcessInspector, + Error, ProcessInspector, maps_reader::{MappingInfo, MapsReaderError}, }; use crate::module_reader::ProcessModuleMemoryReader; @@ -7,55 +7,54 @@ use crate::module_reader::ProcessModuleMemoryReader; pub type ProcessHandle = libc::pid_t; #[derive(Debug)] -pub struct ProcessReader<'a> { - process_inspector: &'a ProcessInspector, -} +pub struct ProcessReader<'a>(&'a dyn ProcessReaderBackend); impl<'a> ProcessReader<'a> { /// Read memory from the process into the given buffer. /// /// Returns the number of bytes read. pub fn read(&self, src: usize, dst: &mut [u8]) -> Result { - match &self.process_inspector.backend { - Backend::Local { backend } => backend - .process_reader() - .read_at(src, dst) - .map_err(Error::Local), - } - .map_err(CopyFromProcessError::Backend) + self.0.read_at(src, dst) } /// Find the address at which a module with the given name is loaded in the process. pub fn find_module( &self, module_name: &str, ) -> Result, FindModuleError> { - MappingInfo::for_pid(self.process_inspector, self.process_inspector.pid, None)? - .into_iter() - .find_map(|m| { - let mmem = ProcessModuleMemoryReader::new(self, m.start_address); - let name = m.name.as_ref().and_then(|s| s.to_str())?; - if name == module_name { - return Some(mmem); - } - // Check whether the SO_NAME matches the module name. - // - // For now, only check the SO_NAME of Android APKS, because libraries may be mapped - // directly from within an APK. See bug 1982902. - #[cfg(target_os = "android")] - if name.ends_with(".apk") { - if let Ok(so_name) = crate::module_reader::read_soname_from_module(&mmem) { - if so_name == name { - return Some(mmem); - } + MappingInfo::for_pid( + self.0.process_inspector(), + self.0 + .process_inspector() + .pid() + .map_err(FindModuleError::GetTargetPidFailed)?, + None, + )? + .into_iter() + .find_map(|m| { + let mmem = ProcessModuleMemoryReader::new(self, m.start_address); + let name = m.name.as_ref().and_then(|s| s.to_str())?; + if name == module_name { + return Some(mmem); + } + // Check whether the SO_NAME matches the module name. + // + // For now, only check the SO_NAME of Android APKS, because libraries may be mapped + // directly from within an APK. See bug 1982902. + #[cfg(target_os = "android")] + if name.ends_with(".apk") { + if let Ok(so_name) = crate::module_reader::read_soname_from_module(&mmem) { + if so_name == name { + return Some(mmem); } } + } - None - }) - .ok_or(FindModuleError::ModuleNotFound) + None + }) + .ok_or(FindModuleError::ModuleNotFound) } - pub(crate) fn new(process_inspector: &'a ProcessInspector) -> Self { - Self { process_inspector } + pub(crate) fn new(backend: &'a dyn ProcessReaderBackend) -> Self { + Self(backend) } } @@ -73,4 +72,11 @@ pub enum FindModuleError { ModuleNotFound, #[error("Failed to read process module mappings")] MappingError(#[from] MapsReaderError), + #[error("Failed to get PID of target process")] + GetTargetPidFailed(#[source] Error), +} + +pub(crate) trait ProcessReaderBackend: core::fmt::Debug { + fn process_inspector(&self) -> &dyn ProcessInspector; + fn read_at(&self, src: usize, dst: &mut [u8]) -> Result; } diff --git a/src/linux/thread_info/aarch64.rs b/src/linux/thread_info/aarch64.rs index 8d68e099..858b13b3 100644 --- a/src/linux/thread_info/aarch64.rs +++ b/src/linux/thread_info/aarch64.rs @@ -34,7 +34,10 @@ impl ThreadInfoAarch64 { out.float_regs[..FP_REG_COUNT].copy_from_slice(&self.fpregs.vregs[..FP_REG_COUNT]); } - pub fn create(process_inspector: &ProcessInspector, tid: Pid) -> Result { + pub fn create( + process_inspector: &dyn ProcessInspector, + tid: Pid, + ) -> Result { let (ppid, tgid) = super::get_ppid_and_tgid(process_inspector, tid)?; let regs = process_inspector .get_gen_regs(tid) diff --git a/src/linux/thread_info/arm.rs b/src/linux/thread_info/arm.rs index 792c23ba..514af28c 100644 --- a/src/linux/thread_info/arm.rs +++ b/src/linux/thread_info/arm.rs @@ -27,7 +27,10 @@ impl ThreadInfoArm { out.float_save.regs = self.fpregs.fpregs; } - pub fn create(process_inspector: &ProcessInspector, tid: Pid) -> Result { + pub fn create( + process_inspector: &dyn ProcessInspector, + tid: Pid, + ) -> Result { let (ppid, tgid) = super::get_ppid_and_tgid(process_inspector, tid)?; let regs = process_inspector .get_gen_regs(tid) diff --git a/src/linux/thread_info/mod.rs b/src/linux/thread_info/mod.rs index 0e5d14b4..81203d2c 100644 --- a/src/linux/thread_info/mod.rs +++ b/src/linux/thread_info/mod.rs @@ -54,7 +54,7 @@ cfg_if::cfg_if! { } } -fn get_ppid_and_tgid(process_inspector: &ProcessInspector, tid: Pid) -> Result<(Pid, Pid)> { +fn get_ppid_and_tgid(process_inspector: &dyn ProcessInspector, tid: Pid) -> Result<(Pid, Pid)> { let mut ppid = -1; let mut tgid = -1; diff --git a/src/linux/thread_info/x86.rs b/src/linux/thread_info/x86.rs index b6e5e95b..007f940e 100644 --- a/src/linux/thread_info/x86.rs +++ b/src/linux/thread_info/x86.rs @@ -17,7 +17,10 @@ pub struct ThreadInfoX86 { } impl ThreadInfoX86 { - pub fn create(process_inspector: &ProcessInspector, tid: Pid) -> Result { + pub fn create( + process_inspector: &dyn ProcessInspector, + tid: Pid, + ) -> Result { let (ppid, tgid) = super::get_ppid_and_tgid(process_inspector, tid)?; let regs = process_inspector .get_gen_regs(tid)