Skip to main content

minidump_writer/
process_reader.rs

1#[cfg(any(target_os = "linux", target_os = "android"))]
2pub use crate::linux::process_reader::*;
3
4#[cfg(target_os = "windows")]
5pub use crate::windows::process_reader::*;
6
7#[cfg(target_os = "macos")]
8pub use crate::mac::process_reader::*;
9
10use std::{ffi::CString, mem::MaybeUninit};
11
12impl ProcessReader<'_> {
13    #[inline]
14    pub fn read_to_vec(
15        &self,
16        src: usize,
17        length: std::num::NonZeroUsize,
18    ) -> Result<Vec<u8>, CopyFromProcessError> {
19        let mut output = vec![0u8; length.into()];
20        let bytes_read = self.read(src, &mut output)?;
21        output.truncate(bytes_read);
22        Ok(output)
23    }
24
25    #[inline]
26    pub fn read_all(&self, src: usize, dst: &mut [u8]) -> Result<(), CopyFromProcessError> {
27        let mut offset = 0;
28        while offset < dst.len() {
29            offset += self.read(src + offset, &mut dst[offset..])?;
30        }
31        Ok(())
32    }
33
34    #[inline]
35    pub fn read_all_to_vec(
36        &self,
37        src: usize,
38        length: usize,
39    ) -> Result<Vec<u8>, CopyFromProcessError> {
40        let mut output = vec![0u8; length];
41        self.read_all(src, &mut output)?;
42        Ok(output)
43    }
44
45    pub fn copy_nul_terminated_string(
46        &self,
47        address: usize,
48    ) -> Result<CString, CopyFromProcessError> {
49        // Try copying the string word-by-word first, this is considerably
50        // faster than one byte at a time.
51        if let Ok(string) = self.copy_nul_terminated_string_word_by_word(address) {
52            return Ok(string);
53        }
54
55        // Reading the string one word at a time failed, let's try again one
56        // byte at a time. It's slow but it might work in situations where the
57        // string alignment causes word-by-word access to straddle page
58        // boundaries.
59        let mut string = Vec::<u8>::new();
60        let mut c = 1u8;
61
62        while c != 0 {
63            self.read(address + string.len(), std::slice::from_mut(&mut c))?;
64            string.push(c);
65        }
66
67        // SAFETY: If we reach this point we've read at least one byte and we
68        // know that the last one we read is nul.
69        Ok(unsafe { CString::from_vec_with_nul_unchecked(string) })
70    }
71
72    fn copy_nul_terminated_string_word_by_word(
73        &self,
74        address: usize,
75    ) -> Result<CString, CopyFromProcessError> {
76        const WORD_SIZE: usize = size_of::<usize>();
77        let mut string = Vec::<u8>::new();
78        let mut word_bytes = [0u8; WORD_SIZE];
79
80        loop {
81            let read_byte_len = self.read(address + string.len(), &mut word_bytes)?;
82            // SAFETY: at most WORD_SIZE bytes are indexed
83            let mut read_bytes =
84                unsafe { word_bytes.get_unchecked(..std::cmp::min(read_byte_len, WORD_SIZE)) };
85            let nul_terminator = read_bytes.iter().position(|&e| e == 0);
86            if let Some(nul_terminator) = nul_terminator {
87                // +1 to include the nul terminator
88                read_bytes = &read_bytes[..nul_terminator + 1];
89            }
90            string.extend(read_bytes);
91
92            if nul_terminator.is_some() {
93                break;
94            }
95        }
96
97        // SAFETY: If we reach this point we've read at least one byte and we
98        // know that the last one we read is nul.
99        Ok(unsafe { CString::from_vec_with_nul_unchecked(string) })
100    }
101
102    #[inline]
103    pub fn copy_object_uninit<T>(
104        &self,
105        src: usize,
106    ) -> Result<MaybeUninit<T>, CopyFromProcessError> {
107        let mut object = MaybeUninit::<T>::uninit();
108        self.read_all(src, uninit_as_bytes_mut(&mut object))?;
109        Ok(object)
110    }
111
112    /// # Safety
113    /// The caller must ensure that the object will be in an initialized, valid state.
114    #[inline]
115    pub unsafe fn copy_object<T>(&self, src: usize) -> Result<T, CopyFromProcessError> {
116        self.copy_object_uninit(src)
117            .map(|object| unsafe { object.assume_init() })
118    }
119
120    #[inline]
121    pub fn copy_array_uninit<T>(
122        &self,
123        src: usize,
124        num: usize,
125    ) -> Result<Vec<MaybeUninit<T>>, CopyFromProcessError> {
126        let mut v = Vec::with_capacity(num);
127        for _ in 0..num {
128            v.push(MaybeUninit::<T>::uninit());
129        }
130        self.read_all(src, uninit_slice_as_bytes_mut(&mut v))?;
131        Ok(v)
132    }
133
134    /// # Safety
135    /// The caller must ensure that the objects will be in an initialized, valid state.
136    #[inline]
137    pub unsafe fn copy_array<T>(
138        &self,
139        src: usize,
140        num: usize,
141    ) -> Result<Vec<T>, CopyFromProcessError> {
142        self.copy_array_uninit(src, num)
143            .map(|v| unsafe { std::mem::transmute::<Vec<MaybeUninit<T>>, Vec<T>>(v) })
144    }
145}
146
147fn uninit_as_bytes_mut<T>(elem: &mut MaybeUninit<T>) -> &mut [u8] {
148    // SAFETY: elem is at least size_of::<T>() bytes, and MaybeUninit<T> has no validity guarantees
149    // (so providing a mutable slice of bytes is sound)
150    unsafe { std::slice::from_raw_parts_mut(elem.as_mut_ptr() as *mut u8, size_of::<T>()) }
151}
152
153fn uninit_slice_as_bytes_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [u8] {
154    // SAFETY: the slice is at least size_of::<T>()*len() bytes, and MaybeUninit<T> has no validity
155    // guarantees (so providing a mutable slice of bytes is sound)
156    unsafe {
157        std::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, size_of::<T>() * slice.len())
158    }
159}
160
161/*
162#[derive(Debug, Error)]
163pub enum ProcessReaderError {
164    #[error("Could not convert address {0}")]
165    ConvertAddressError(#[from] std::num::TryFromIntError),
166    #[error("Could not parse address {0}")]
167    ParseAddressError(#[from] std::num::ParseIntError),
168    #[cfg(target_os = "windows")]
169    #[error("Cannot enumerate the target process's modules")]
170    EnumProcessModulesError,
171    #[error("goblin failed to parse a module")]
172    GoblinError(#[from] goblin::error::Error),
173    #[error("Address was out of bounds")]
174    InvalidAddress,
175    #[error("Could not read from the target process address space")]
176    ReadFromProcessError(#[from] ReadError),
177    #[cfg(any(target_os = "windows", target_os = "macos"))]
178    #[error("Section was not found")]
179    SectionNotFound,
180    #[cfg(any(target_os = "linux", target_os = "android"))]
181    #[error("Could not attach to the target process")]
182    AttachError(#[from] PtraceError),
183    #[cfg(any(target_os = "linux", target_os = "android"))]
184    #[error("Note not found")]
185    NoteNotFound,
186    #[cfg(any(target_os = "linux", target_os = "android"))]
187    #[error("SONAME not found")]
188    SoNameNotFound,
189    #[cfg(any(target_os = "linux", target_os = "android"))]
190    #[error("waitpid() failed when attaching to the process")]
191    WaitPidError,
192    #[cfg(any(target_os = "linux", target_os = "android"))]
193    #[error("Could not parse a line in /proc/<pid>/maps")]
194    ProcMapsParseError,
195    #[error("Module not found")]
196    ModuleNotFound,
197    #[cfg(any(target_os = "linux", target_os = "android"))]
198    #[error("IO error for file {0}")]
199    IOError(#[from] std::io::Error),
200    #[cfg(target_os = "macos")]
201    #[error("Failure when requesting the task information")]
202    TaskInfoError,
203    #[cfg(target_os = "macos")]
204    #[error("The task dyld information format is unknown or invalid")]
205    ImageFormatError,
206}
207
208#[derive(Debug, Error)]
209pub enum ReadError {
210    #[cfg(target_os = "macos")]
211    #[error("mach call failed")]
212    MachError,
213    #[cfg(any(target_os = "linux", target_os = "android"))]
214    #[error("ptrace-specific error")]
215    PtraceError(#[from] PtraceError),
216    #[cfg(target_os = "windows")]
217    #[error("ReadProcessMemory failed")]
218    ReadProcessMemoryError,
219}
220
221#[cfg(any(target_os = "linux", target_os = "android"))]
222#[derive(Debug, Error)]
223pub enum PtraceError {
224    #[error("Could not read from the target process address space")]
225    ReadError(#[source] std::io::Error),
226    #[error("Could not trace the process")]
227    TraceError(#[source] std::io::Error),
228}
229*/