diff --git a/src/linux/maps_reader.rs b/src/linux/maps_reader.rs index 57986db1..ce163b81 100644 --- a/src/linux/maps_reader.rs +++ b/src/linux/maps_reader.rs @@ -2,7 +2,7 @@ use { super::{ auxv::AuxvType, module_reader::ModuleReaderError, - process_inspection::{self, ProcessInspector}, + process_inspection::{self, Backend, ProcessInspector}, serializers::*, }, crate::serializers::*, @@ -136,12 +136,12 @@ fn sanitize_path(pathname: OsString) -> OsString { impl MappingInfo { /// Get the mappings for the given process. pub fn for_pid( - process_inspector: &ProcessInspector, + backend: &Backend, pid: i32, linux_gate_loc: Option, ) -> Result> { let maps_path = format!("/proc/{}/maps", pid); - let maps_file = process_inspector + let maps_file = backend .read_file(&maps_path) .map_err(MapsReaderError::ReadFileFailed)?; let maps = MemoryMaps::from_read(maps_file)?; diff --git a/src/linux/minidump_writer/mod.rs b/src/linux/minidump_writer/mod.rs index ee03d8b3..18f085df 100644 --- a/src/linux/minidump_writer/mod.rs +++ b/src/linux/minidump_writer/mod.rs @@ -702,7 +702,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.backend, self.process_id, self.auxv.get_linux_gate_address(), ) diff --git a/src/linux/module_reader.rs b/src/linux/module_reader.rs index 58452846..9fa74d95 100644 --- a/src/linux/module_reader.rs +++ b/src/linux/module_reader.rs @@ -604,6 +604,22 @@ impl<'a> ReadModuleMemory for ProcessModuleMemoryReader<'a> { } } +impl<'a> ReadModuleMemory for &ProcessModuleMemoryReader<'a> { + fn read(&self, offset: u64, length: u64) -> Result, ModuleMemoryReadError> { + ProcessModuleMemoryReader::read(self, offset, length) + } + fn absolute_to_relative(&self, addr: u64) -> Option { + addr.checked_sub(self.start_address) + } + /// Calculates the absolute address of the specified relative address + fn relative_to_absolute(&self, addr: u64) -> Option { + self.start_address.checked_add(addr) + } + fn is_process_memory(&self) -> bool { + true + } +} + #[cfg(test)] mod test { use super::*; diff --git a/src/linux/process_inspection/mod.rs b/src/linux/process_inspection/mod.rs index c74ea597..e8615784 100644 --- a/src/linux/process_inspection/mod.rs +++ b/src/linux/process_inspection/mod.rs @@ -22,7 +22,7 @@ pub mod process_reader; pub struct ProcessInspector { pid: libc::pid_t, process_reader: ProcessReader, - backend: Rc, + pub(crate) backend: Rc, } #[derive(Debug)] @@ -30,13 +30,25 @@ pub enum Backend { Local(local::Backend), } +impl Backend { + pub fn read_file(&self, path: impl Into) -> Result { + let c_path = CString::new(path.into().into_os_string().into_vec()).unwrap(); + match self { + Backend::Local(l) => l + .read_file(&c_path) + .map(FileReader::Local) + .map_err(Error::Local), + } + } +} + impl ProcessInspector { pub fn local(pid: libc::pid_t) -> Self { let backend = Rc::new(Backend::Local(local::Backend::new(pid))); ProcessInspector { pid, - process_reader: ProcessReader::new(pid, Rc::clone(&backend)), + process_reader: ProcessReader::new_with_backend(pid, Rc::clone(&backend)), backend, } } @@ -93,13 +105,7 @@ impl ProcessInspector { } pub fn read_file(&self, path: impl Into) -> Result { - let c_path = CString::new(path.into().into_os_string().into_vec()).unwrap(); - match &*self.backend { - Backend::Local(l) => l - .read_file(&c_path) - .map(FileReader::Local) - .map_err(Error::Local), - } + self.backend.read_file(path) } pub fn read_dir(&self, path: impl Into) -> Result { @@ -199,7 +205,7 @@ impl Iterator for DirReader { #[doc(hidden)] impl ProcessInspector { pub fn force_pr_reset(&mut self) { - self.process_reader = ProcessReader::new(self.pid, Rc::clone(&self.backend)) + self.process_reader = ProcessReader::new_with_backend(self.pid, Rc::clone(&self.backend)) } pub fn force_pr_virtual_mem(&mut self) { self.process_reader = ProcessReader::for_virtual_mem(self.pid, Rc::clone(&self.backend)) diff --git a/src/linux/process_inspection/process_reader.rs b/src/linux/process_inspection/process_reader.rs index a61e8376..2d3e2fa8 100644 --- a/src/linux/process_inspection/process_reader.rs +++ b/src/linux/process_inspection/process_reader.rs @@ -1,9 +1,13 @@ use { super::{Backend, Error, FileReader}, + crate::linux::maps_reader::{MappingInfo, MapsReaderError}, + crate::module_reader::ProcessModuleMemoryReader, core::{ffi::c_long, mem}, std::{ffi::CString, rc::Rc, sync::OnceLock}, }; +pub type ProcessHandle = libc::pid_t; + #[derive(Debug)] enum Style { /// Uses [`process_vm_readv`](https://linux.die.net/man/2/process_vm_readv) @@ -49,6 +53,14 @@ pub enum CopyFromProcessError { InvalidArgument, } +#[derive(Debug, thiserror::Error, serde::Serialize)] +pub enum FindModuleError { + #[error("Module not found")] + ModuleNotFound, + #[error("Failed to read process module mappings")] + MappingError(#[from] MapsReaderError), +} + #[derive(Debug, thiserror::Error, serde::Serialize)] pub enum StrategyError { #[error(transparent)] @@ -82,7 +94,15 @@ impl ProcessReader { /// Creates a [`Self`] for the specified process id, the method used will /// be probed for on the first access #[inline] - pub(super) fn new(pid: libc::pid_t, backend: Rc) -> Self { + pub fn new(pid: ProcessHandle) -> Self { + Self::new_with_backend( + pid, + Rc::new(Backend::Local(super::local::Backend::new(pid))), + ) + } + + #[inline] + pub(super) fn new_with_backend(pid: libc::pid_t, backend: Rc) -> Self { Self { pid, style: OnceLock::default(), @@ -231,4 +251,34 @@ impl ProcessReader { Ok(dst.len()) } + + pub fn find_module( + &self, + module_name: &str, + ) -> Result, FindModuleError> { + MappingInfo::for_pid(&self.backend, self.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); + } + } + } + + None + }) + .ok_or(FindModuleError::ModuleNotFound) + } } diff --git a/src/process_reader.rs b/src/process_reader.rs index 38d75ba9..c78ae92a 100644 --- a/src/process_reader.rs +++ b/src/process_reader.rs @@ -157,73 +157,3 @@ fn uninit_slice_as_bytes_mut(slice: &mut [MaybeUninit]) -> &mut [u8] { std::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of::() * slice.len()) } } - -/* -#[derive(Debug, Error)] -pub enum ProcessReaderError { - #[error("Could not convert address {0}")] - ConvertAddressError(#[from] std::num::TryFromIntError), - #[error("Could not parse address {0}")] - ParseAddressError(#[from] std::num::ParseIntError), - #[cfg(target_os = "windows")] - #[error("Cannot enumerate the target process's modules")] - EnumProcessModulesError, - #[error("goblin failed to parse a module")] - GoblinError(#[from] goblin::error::Error), - #[error("Address was out of bounds")] - InvalidAddress, - #[error("Could not read from the target process address space")] - ReadFromProcessError(#[from] ReadError), - #[cfg(any(target_os = "windows", target_os = "macos"))] - #[error("Section was not found")] - SectionNotFound, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("Could not attach to the target process")] - AttachError(#[from] PtraceError), - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("Note not found")] - NoteNotFound, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("SONAME not found")] - SoNameNotFound, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("waitpid() failed when attaching to the process")] - WaitPidError, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("Could not parse a line in /proc//maps")] - ProcMapsParseError, - #[error("Module not found")] - ModuleNotFound, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("IO error for file {0}")] - IOError(#[from] std::io::Error), - #[cfg(target_os = "macos")] - #[error("Failure when requesting the task information")] - TaskInfoError, - #[cfg(target_os = "macos")] - #[error("The task dyld information format is unknown or invalid")] - ImageFormatError, -} - -#[derive(Debug, Error)] -pub enum ReadError { - #[cfg(target_os = "macos")] - #[error("mach call failed")] - MachError, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("ptrace-specific error")] - PtraceError(#[from] PtraceError), - #[cfg(target_os = "windows")] - #[error("ReadProcessMemory failed")] - ReadProcessMemoryError, -} - -#[cfg(any(target_os = "linux", target_os = "android"))] -#[derive(Debug, Error)] -pub enum PtraceError { - #[error("Could not read from the target process address space")] - ReadError(#[source] std::io::Error), - #[error("Could not trace the process")] - TraceError(#[source] std::io::Error), -} -*/