summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs')
-rw-r--r--toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs193
1 files changed, 193 insertions, 0 deletions
diff --git a/toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs b/toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs
new file mode 100644
index 0000000000..ffdcd95937
--- /dev/null
+++ b/toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs
@@ -0,0 +1,193 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use std::{
+ convert::TryInto,
+ mem::{size_of, MaybeUninit},
+ ptr::null_mut,
+};
+
+use winapi::{
+ shared::{
+ basetsd::SIZE_T,
+ minwindef::{DWORD, FALSE, HMODULE},
+ },
+ um::{
+ memoryapi::ReadProcessMemory,
+ psapi::{K32EnumProcessModules, K32GetModuleInformation, MODULEINFO},
+ },
+};
+
+use crate::{
+ errors::{FindAnnotationsAddressError, ReadError, RetrievalError},
+ ProcessHandle,
+};
+
+use super::ProcessReader;
+
+impl ProcessReader {
+ pub fn new(process: ProcessHandle) -> Result<ProcessReader, RetrievalError> {
+ Ok(ProcessReader { process })
+ }
+
+ pub fn find_annotations(&self) -> Result<usize, FindAnnotationsAddressError> {
+ let modules = self.get_module_list()?;
+
+ modules
+ .iter()
+ .find_map(|&module| {
+ self.get_module_info(module).and_then(|info| {
+ self.find_annotations_in_module(
+ info.lpBaseOfDll as usize,
+ info.SizeOfImage as usize,
+ )
+ .ok()
+ })
+ })
+ .ok_or(FindAnnotationsAddressError::InvalidAddress)
+ }
+
+ fn get_module_list(&self) -> Result<Vec<HMODULE>, FindAnnotationsAddressError> {
+ let mut module_num: usize = 100;
+ let mut required_buffer_size: DWORD = 0;
+ let mut module_array = Vec::<MaybeUninit<HMODULE>>::with_capacity(module_num);
+
+ loop {
+ let buffer_size: DWORD = (module_num * size_of::<HMODULE>()).try_into()?;
+ let res = unsafe {
+ K32EnumProcessModules(
+ self.process,
+ module_array.as_mut_ptr() as *mut _,
+ buffer_size,
+ &mut required_buffer_size as *mut _,
+ )
+ };
+
+ module_num = required_buffer_size as usize / size_of::<HMODULE>();
+
+ if res == 0 {
+ if required_buffer_size > buffer_size {
+ module_array = Vec::<MaybeUninit<HMODULE>>::with_capacity(module_num);
+ } else {
+ return Err(FindAnnotationsAddressError::EnumProcessModulesError);
+ }
+ } else {
+ break;
+ }
+ }
+
+ // SAFETY: module_array has been filled by K32EnumProcessModules()
+ let module_array: Vec<HMODULE> = unsafe {
+ module_array.set_len(module_num);
+ std::mem::transmute(module_array)
+ };
+
+ Ok(module_array)
+ }
+
+ fn get_module_info(&self, module: HMODULE) -> Option<MODULEINFO> {
+ let mut info: MaybeUninit<MODULEINFO> = MaybeUninit::uninit();
+ let res = unsafe {
+ K32GetModuleInformation(
+ self.process,
+ module,
+ info.as_mut_ptr(),
+ size_of::<MODULEINFO>() as DWORD,
+ )
+ };
+
+ if res == 0 {
+ None
+ } else {
+ let info = unsafe { info.assume_init() };
+ Some(info)
+ }
+ }
+
+ fn find_annotations_in_module(
+ &self,
+ module_address: usize,
+ size: usize,
+ ) -> Result<usize, FindAnnotationsAddressError> {
+ // We read only the first page from the module, this should be more than
+ // enough to read the header and section list. In the future we might do
+ // this incrementally but for now goblin requires an array to parse
+ // so we can't do it just yet.
+ let page_size = 4096;
+ if size < page_size {
+ // Don't try to read from the target module if it's too small
+ return Err(FindAnnotationsAddressError::ReadError(
+ ReadError::ReadProcessMemoryError,
+ ));
+ }
+
+ let bytes = self.copy_array(module_address as _, 4096)?;
+ let header = goblin::pe::header::Header::parse(&bytes)?;
+
+ // Skip the PE header so we can parse the sections
+ let optional_header_offset = header.dos_header.pe_pointer as usize
+ + goblin::pe::header::SIZEOF_PE_MAGIC
+ + goblin::pe::header::SIZEOF_COFF_HEADER;
+ let offset =
+ &mut (optional_header_offset + header.coff_header.size_of_optional_header as usize);
+
+ let sections = header.coff_header.sections(&bytes, offset)?;
+
+ for section in sections {
+ if section.name.eq(mozannotation_client::ANNOTATION_SECTION) {
+ let address = module_address.checked_add(section.virtual_address as usize);
+ return address.ok_or(FindAnnotationsAddressError::InvalidAddress);
+ }
+ }
+
+ Err(FindAnnotationsAddressError::SectionNotFound)
+ }
+
+ pub fn copy_object_shallow<T>(&self, src: usize) -> Result<MaybeUninit<T>, ReadError> {
+ let mut object = MaybeUninit::<T>::uninit();
+ let res = unsafe {
+ ReadProcessMemory(
+ self.process,
+ src as _,
+ object.as_mut_ptr() as _,
+ size_of::<T>() as SIZE_T,
+ null_mut(),
+ )
+ };
+
+ if res != FALSE {
+ Ok(object)
+ } else {
+ Err(ReadError::ReadProcessMemoryError)
+ }
+ }
+
+ pub fn copy_object<T>(&self, src: usize) -> Result<T, ReadError> {
+ let object = self.copy_object_shallow(src)?;
+ Ok(unsafe { object.assume_init() })
+ }
+
+ pub fn copy_array<T>(&self, src: usize, num: usize) -> Result<Vec<T>, ReadError> {
+ let num_of_bytes = num * size_of::<T>();
+ let mut array: Vec<MaybeUninit<T>> = Vec::with_capacity(num);
+ let res = unsafe {
+ ReadProcessMemory(
+ self.process,
+ src as _,
+ array.as_mut_ptr() as _,
+ num_of_bytes as SIZE_T,
+ null_mut(),
+ )
+ };
+
+ if res != FALSE {
+ unsafe {
+ array.set_len(num);
+ Ok(std::mem::transmute(array))
+ }
+ } else {
+ Err(ReadError::ReadProcessMemoryError)
+ }
+ }
+}