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*/