From 7e76ab950d7443d4c8d6f019cb73943e9ab370fb Mon Sep 17 00:00:00 2001 From: Alex Franchuk Date: Tue, 13 Oct 2026 21:27:00 -0400 Subject: [PATCH] Use fallible Vec allocation when the capacity comes from input data. Closes #243. --- src/lib.rs | 1 + .../process_inspection/process_reader.rs | 5 +++- src/mac/module_reader.rs | 7 ++++-- src/mac/process_reader.rs | 20 +++++++++------ src/process_reader.rs | 3 ++- src/vec_extension.rs | 18 +++++++++++++ src/windows/process_reader.rs | 25 ++++++++++++------- 7 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 src/vec_extension.rs diff --git a/src/lib.rs b/src/lib.rs index 9e843c3a..89348f57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ pub mod module_reader; pub mod process_reader; mod serializers; +mod vec_extension; /// Re-export of crash-context pub use ::crash_context; diff --git a/src/linux/process_inspection/process_reader.rs b/src/linux/process_inspection/process_reader.rs index 092e2521..b4cf283c 100644 --- a/src/linux/process_inspection/process_reader.rs +++ b/src/linux/process_inspection/process_reader.rs @@ -3,6 +3,7 @@ use super::{ maps_reader::{MappingInfo, MapsReaderError}, }; use crate::module_reader::ProcessModuleMemoryReader; +use crate::vec_extension::*; use plain::Plain; @@ -105,7 +106,7 @@ impl<'a> ProcessReader<'a> { mut address: usize, count: usize, ) -> Result, CopyFromProcessError> { - let mut v = Vec::with_capacity(count); + let mut v = vec_with_capacity(count)?; for _ in 0..count { v.push(self.read_pod(address)?); address += std::mem::size_of::(); @@ -182,6 +183,8 @@ pub enum CopyFromProcessError { Backend(Error), #[error("an invalid argument was passed")] InvalidArgument, + #[error("failed to pre-allocate memory")] + CapacityError(#[from] TryReserveError), } #[derive(Debug, thiserror::Error, serde::Serialize)] diff --git a/src/mac/module_reader.rs b/src/mac/module_reader.rs index 21d31ca5..488ad0c0 100644 --- a/src/mac/module_reader.rs +++ b/src/mac/module_reader.rs @@ -1,5 +1,6 @@ use crate::module_reader::{ModuleMemoryReadError, ProcessModuleMemoryReader}; use crate::serializers::*; +use crate::vec_extension::*; use goblin::container::{Container, Ctx, Endian}; use goblin::mach::{ self, @@ -34,6 +35,8 @@ pub enum ModuleReaderError { ), #[error("failed to derive parsing context from MachO memory")] MissingCtx, + #[error("failed to pre-allocate memory")] + AllocError(#[from] TryReserveError), } macro_rules! read_scroll { @@ -47,7 +50,7 @@ macro_rules! read_scroll { macro_rules! read_scroll_array { ( $T:ty , $mem:expr , $offset:expr , $count:expr , $ctx:expr ) => {{ let bytes = $mem.read($offset, (<$T>::size_with(&$ctx) * $count) as u64)?; - let mut vals = Vec::with_capacity($count); + let mut vals = vec_with_capacity($count)?; let mut offset = 0; for _ in 0..$count { let (value, bytes_read) = <$T>::try_from_ctx(&bytes[offset..], $ctx)?; @@ -79,7 +82,7 @@ impl ProcessModuleMemoryReader<'_> { Error: From, { let bytes = self.read(offset, (T::size_with(&ctx) * count) as u64)?; - let mut vals = Vec::with_capacity(count); + let mut vals = vec_with_capacity(count)?; let mut offset = 0; for _ in 0..count { let (value, bytes_read) = match T::try_from_ctx(&bytes[offset..], ctx) { diff --git a/src/mac/process_reader.rs b/src/mac/process_reader.rs index a18e1287..c7fc3f46 100644 --- a/src/mac/process_reader.rs +++ b/src/mac/process_reader.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use { - crate::module_reader::ProcessModuleMemoryReader, + crate::{module_reader::ProcessModuleMemoryReader, vec_extension::TryReserveError}, mach2::{ kern_return::KERN_SUCCESS, task::task_info, @@ -57,12 +57,16 @@ struct ImageInfo { } #[derive(Debug, thiserror::Error, serde::Serialize)] -#[error("Copy from process {child} failed (source {src}, length: {length})")] -pub struct CopyFromProcessError { - pub child: ProcessHandle, - pub src: usize, - pub length: usize, - pub kern_return: mach2::kern_return::kern_return_t, +pub enum CopyFromProcessError { + #[error("Copy from process {child} failed (source {src}, length: {length})")] + ReadError { + child: ProcessHandle, + src: usize, + length: usize, + kern_return: mach2::kern_return::kern_return_t, + }, + #[error("failed to pre-allocate memory")] + AllocError(#[from] TryReserveError), } #[derive(Debug, thiserror::Error)] @@ -100,7 +104,7 @@ impl ProcessReader<'_> { if res == KERN_SUCCESS { Ok(size as usize) } else { - Err(CopyFromProcessError { + Err(CopyFromProcessError::ReadError { child: self.process, src, length: dst.len(), diff --git a/src/process_reader.rs b/src/process_reader.rs index 706017df..c9fae991 100644 --- a/src/process_reader.rs +++ b/src/process_reader.rs @@ -7,6 +7,7 @@ pub use crate::windows::process_reader::*; #[cfg(target_os = "macos")] pub use crate::mac::process_reader::*; +use crate::vec_extension::vec_with_capacity; use std::{ffi::CString, mem::MaybeUninit}; impl ProcessReader<'_> { @@ -123,7 +124,7 @@ impl ProcessReader<'_> { src: usize, num: usize, ) -> Result>, CopyFromProcessError> { - let mut v = Vec::with_capacity(num); + let mut v = vec_with_capacity(num)?; for _ in 0..num { v.push(MaybeUninit::::uninit()); } diff --git a/src/vec_extension.rs b/src/vec_extension.rs new file mode 100644 index 00000000..041c0138 --- /dev/null +++ b/src/vec_extension.rs @@ -0,0 +1,18 @@ +//! Vec extension functions/methods. + +/// We need a TryReserveError that is serializable, so we create one here. +#[derive(Debug, thiserror::Error, serde::Serialize)] +#[error("{}", .0)] +pub struct TryReserveError(String); + +impl From for TryReserveError { + fn from(value: std::collections::TryReserveError) -> Self { + Self(value.to_string()) + } +} + +pub fn vec_with_capacity(capacity: usize) -> Result, TryReserveError> { + let mut v = Vec::new(); + v.try_reserve_exact(capacity)?; + Ok(v) +} diff --git a/src/windows/process_reader.rs b/src/windows/process_reader.rs index 59f6d686..e9d52a82 100644 --- a/src/windows/process_reader.rs +++ b/src/windows/process_reader.rs @@ -1,6 +1,9 @@ use { super::ffi::{self, HMODULE}, - crate::{module_reader::ProcessModuleMemoryReader, serializers::serialize_io_error}, + crate::{ + module_reader::ProcessModuleMemoryReader, serializers::serialize_io_error, + vec_extension::TryReserveError, + }, std::{ convert::TryInto, ffi::OsString, @@ -18,13 +21,17 @@ pub struct ProcessReader<'a> { } #[derive(Debug, thiserror::Error, serde::Serialize)] -#[error("Copy from process {child} failed (source {src}, length: {length})")] -pub struct CopyFromProcessError { - pub child: ProcessHandle, - pub src: usize, - pub length: usize, - #[serde(serialize_with = "serialize_io_error")] - pub error: std::io::Error, +pub enum CopyFromProcessError { + #[error("Copy from process {child} failed (source {src}, length: {length})")] + ReadError { + child: ProcessHandle, + src: usize, + length: usize, + #[serde(serialize_with = "serialize_io_error")] + error: std::io::Error, + }, + #[error("failed to pre-allocate memory")] + AllocError(#[from] TryReserveError), } #[derive(Debug, thiserror::Error, serde::Serialize)] @@ -60,7 +67,7 @@ impl ProcessReader<'_> { if res != ffi::FALSE { Ok(size) } else { - Err(CopyFromProcessError { + Err(CopyFromProcessError::ReadError { child: self.process, src, length: dst.len(),