diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:42 +0000 |
commit | da4c7e7ed675c3bf405668739c3012d140856109 (patch) | |
tree | cdd868dba063fecba609a1d819de271f0d51b23e /toolkit/crashreporter/mozannotation_server/src | |
parent | Adding upstream version 125.0.3. (diff) | |
download | firefox-da4c7e7ed675c3bf405668739c3012d140856109.tar.xz firefox-da4c7e7ed675c3bf405668739c3012d140856109.zip |
Adding upstream version 126.0.upstream/126.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/crashreporter/mozannotation_server/src')
6 files changed, 75 insertions, 875 deletions
diff --git a/toolkit/crashreporter/mozannotation_server/src/errors.rs b/toolkit/crashreporter/mozannotation_server/src/errors.rs index 037d432e5e..b8103ad4e4 100644 --- a/toolkit/crashreporter/mozannotation_server/src/errors.rs +++ b/toolkit/crashreporter/mozannotation_server/src/errors.rs @@ -5,77 +5,13 @@ use thiserror::Error; #[derive(Debug, Error)] -pub enum RetrievalError { - #[error("The process handle/PID was invalid")] - InvalidProcessHandle, - #[error("Could not find the address of the annotations vector")] - AnnotationTableNotFound(#[from] FindAnnotationsAddressError), +pub enum AnnotationsRetrievalError { + #[error("Address was out of bounds")] + InvalidAddress, #[error("Corrupt or wrong annotation table")] InvalidAnnotationTable, #[error("The data read from the target process is invalid")] InvalidData, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("Could not attach to the target process")] - AttachError(#[from] PtraceError), - #[error("Could not read from the target process address space")] - ReadFromProcessError(#[from] ReadError), - #[error("waitpid() failed when attaching to the process")] - WaitPidError, -} - -#[derive(Debug, Error)] -pub enum FindAnnotationsAddressError { - #[error("Could not convert address {0}")] - ConvertAddressError(#[from] std::num::TryFromIntError), - #[error("goblin failed to parse a module")] - GoblinError(#[from] goblin::error::Error), - #[error("Address was out of bounds")] - InvalidAddress, - #[error("IO error for file {0}")] - IOError(#[from] std::io::Error), - #[error("Could not find the address of the annotations vector")] - NotFound, - #[error("Could not parse address {0}")] - ParseAddressError(#[from] std::num::ParseIntError), - #[error("Could not parse a line in /proc/<pid>/maps")] - ProcMapsParseError, - #[cfg(any(target_os = "linux", target_os = "android"))] - #[error("Program header was not found")] - ProgramHeaderNotFound, - #[cfg(target_os = "windows")] - #[error("Section was not found")] - SectionNotFound, - #[cfg(target_os = "windows")] - #[error("Cannot enumerate the target process's modules")] - EnumProcessModulesError, - #[error("Could not read memory from the target process")] - ReadError(#[from] ReadError), - #[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(any(target_os = "linux", target_os = "android"))] - #[error("ptrace-specific error")] - PtraceError(#[from] PtraceError), - #[cfg(target_os = "windows")] - #[error("ReadProcessMemory failed")] - ReadProcessMemoryError, - #[cfg(target_os = "macos")] - #[error("mach call failed")] - MachError, -} - -#[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), + #[error("Could not execute operation on the target process")] + ProcessReaderError(#[from] process_reader::error::ProcessReaderError), } diff --git a/toolkit/crashreporter/mozannotation_server/src/lib.rs b/toolkit/crashreporter/mozannotation_server/src/lib.rs index 6ed5b3cc41..27682247f8 100644 --- a/toolkit/crashreporter/mozannotation_server/src/lib.rs +++ b/toolkit/crashreporter/mozannotation_server/src/lib.rs @@ -3,12 +3,18 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ mod errors; -mod process_reader; use crate::errors::*; +#[cfg(any(target_os = "linux", target_os = "android"))] +use memoffset::offset_of; +use process_reader::error::ProcessReaderError; use process_reader::ProcessReader; +#[cfg(any(target_os = "windows", target_os = "macos"))] +use mozannotation_client::ANNOTATION_SECTION; use mozannotation_client::{Annotation, AnnotationContents, AnnotationMutex}; +#[cfg(any(target_os = "linux", target_os = "android"))] +use mozannotation_client::{MozAnnotationNote, ANNOTATION_TYPE}; use std::cmp::min; use std::iter::FromIterator; use std::mem::{size_of, ManuallyDrop}; @@ -29,12 +35,7 @@ pub struct CAnnotation { data: AnnotationData, } -#[cfg(target_os = "windows")] -type ProcessHandle = winapi::shared::ntdef::HANDLE; -#[cfg(any(target_os = "linux", target_os = "android"))] -type ProcessHandle = libc::pid_t; -#[cfg(any(target_os = "macos"))] -type ProcessHandle = mach2::mach_types::task_t; +pub type ProcessHandle = process_reader::ProcessHandle; /// Return the annotations of a given process. /// @@ -68,19 +69,23 @@ pub unsafe extern "C" fn mozannotation_free(ptr: *mut ThinVec<CAnnotation>) { pub fn retrieve_annotations( process: ProcessHandle, max_annotations: usize, -) -> Result<Box<ThinVec<CAnnotation>>, RetrievalError> { +) -> Result<Box<ThinVec<CAnnotation>>, AnnotationsRetrievalError> { let reader = ProcessReader::new(process)?; - let address = reader.find_annotations()?; + let address = find_annotations(&reader)?; - let mut mutex = reader.copy_object_shallow::<AnnotationMutex>(address)?; + let mut mutex = reader + .copy_object_shallow::<AnnotationMutex>(address) + .map_err(ProcessReaderError::from)?; let mutex = unsafe { mutex.assume_init_mut() }; // TODO: we should clear the poison value here before getting the mutex // contents. Right now we have to fail if the mutex was poisoned. - let annotation_table = mutex.get_mut().map_err(|_e| RetrievalError::InvalidData)?; + let annotation_table = mutex + .get_mut() + .map_err(|_e| AnnotationsRetrievalError::InvalidData)?; if !annotation_table.verify() { - return Err(RetrievalError::InvalidAnnotationTable); + return Err(AnnotationsRetrievalError::InvalidAnnotationTable); } let vec_pointer = annotation_table.get_ptr(); @@ -97,8 +102,47 @@ pub fn retrieve_annotations( Ok(Box::new(annotations)) } +fn find_annotations(reader: &ProcessReader) -> Result<usize, AnnotationsRetrievalError> { + #[cfg(any(target_os = "linux", target_os = "android"))] + { + let libxul_address = reader.find_module("libxul.so")?; + let note_address = reader.find_program_note( + libxul_address, + ANNOTATION_TYPE, + size_of::<MozAnnotationNote>(), + )?; + + let note = reader + .copy_object::<MozAnnotationNote>(note_address) + .map_err(ProcessReaderError::from)?; + let desc = note.desc; + let ehdr = (-note.ehdr) as usize; + let offset = desc + ehdr + - (offset_of!(MozAnnotationNote, ehdr) - offset_of!(MozAnnotationNote, desc)); + + usize::checked_add(libxul_address, offset).ok_or(AnnotationsRetrievalError::InvalidAddress) + } + #[cfg(any(target_os = "macos"))] + { + let libxul_address = reader.find_module("XUL")?; + reader + .find_section(libxul_address, ANNOTATION_SECTION) + .map_err(AnnotationsRetrievalError::from) + } + #[cfg(any(target_os = "windows"))] + { + let libxul_address = reader.find_module("xul.dll")?; + reader + .find_section(libxul_address, ANNOTATION_SECTION) + .map_err(AnnotationsRetrievalError::from) + } +} + // Read an annotation from the given address -fn read_annotation(reader: &ProcessReader, address: usize) -> Result<CAnnotation, ReadError> { +fn read_annotation( + reader: &ProcessReader, + address: usize, +) -> Result<CAnnotation, process_reader::error::ReadError> { let raw_annotation = ManuallyDrop::new(reader.copy_object::<Annotation>(address)?); let mut annotation = CAnnotation { id: raw_annotation.id, @@ -116,16 +160,16 @@ fn read_annotation(reader: &ProcessReader, address: usize) -> Result<CAnnotation annotation.data = AnnotationData::ByteBuffer(string); } AnnotationContents::CStringPointer => { - let buffer = copy_null_terminated_string_pointer(reader, raw_annotation.address)?; - annotation.data = AnnotationData::ByteBuffer(buffer); + let string = copy_null_terminated_string_pointer(reader, raw_annotation.address)?; + annotation.data = AnnotationData::ByteBuffer(string); } AnnotationContents::CString => { - let buffer = copy_null_terminated_string(reader, raw_annotation.address)?; - annotation.data = AnnotationData::ByteBuffer(buffer); + let string = copy_null_terminated_string(reader, raw_annotation.address)?; + annotation.data = AnnotationData::ByteBuffer(string); } AnnotationContents::ByteBuffer(size) | AnnotationContents::OwnedByteBuffer(size) => { - let buffer = copy_bytebuffer(reader, raw_annotation.address, size)?; - annotation.data = AnnotationData::ByteBuffer(buffer); + let string = copy_bytebuffer(reader, raw_annotation.address, size)?; + annotation.data = AnnotationData::ByteBuffer(string); } }; @@ -135,7 +179,7 @@ fn read_annotation(reader: &ProcessReader, address: usize) -> Result<CAnnotation fn copy_null_terminated_string_pointer( reader: &ProcessReader, address: usize, -) -> Result<ThinVec<u8>, ReadError> { +) -> Result<ThinVec<u8>, process_reader::error::ReadError> { let buffer_address = reader.copy_object::<usize>(address)?; copy_null_terminated_string(reader, buffer_address) } @@ -143,56 +187,15 @@ fn copy_null_terminated_string_pointer( fn copy_null_terminated_string( reader: &ProcessReader, address: usize, -) -> Result<ThinVec<u8>, ReadError> { - // Try copying the string word-by-word first, this is considerably faster - // than one byte at a time. - if let Ok(string) = copy_null_terminated_string_word_by_word(reader, address) { - return Ok(string); - } - - // Reading the string one word at a time failed, let's try again one byte - // at a time. It's slow but it might work in situations where the string - // alignment causes word-by-word access to straddle page boundaries. - let mut length = 0; - let mut string = ThinVec::<u8>::new(); - - loop { - let char = reader.copy_object::<u8>(address + length)?; - length += 1; - string.push(char); - - if char == 0 { - break; - } - } - - Ok(string) +) -> Result<ThinVec<u8>, process_reader::error::ReadError> { + let string = reader.copy_null_terminated_string(address)?; + Ok(ThinVec::<u8>::from(string.as_bytes())) } -fn copy_null_terminated_string_word_by_word( +fn copy_nscstring( reader: &ProcessReader, address: usize, -) -> Result<ThinVec<u8>, ReadError> { - const WORD_SIZE: usize = size_of::<usize>(); - let mut length = 0; - let mut string = ThinVec::<u8>::new(); - - loop { - let array = reader.copy_array::<u8>(address + length, WORD_SIZE)?; - let null_terminator = array.iter().position(|&e| e == 0); - length += null_terminator.unwrap_or(WORD_SIZE); - string.extend(array.into_iter()); - - if null_terminator.is_some() { - string.truncate(length); - break; - } - } - - Ok(string) -} - -fn copy_nscstring(reader: &ProcessReader, address: usize) -> Result<ThinVec<u8>, ReadError> { +) -> Result<ThinVec<u8>, process_reader::error::ReadError> { // HACK: This assumes the layout of the nsCString object let length_address = address + size_of::<usize>(); let length = reader.copy_object::<u32>(length_address)?; @@ -211,7 +214,7 @@ fn copy_bytebuffer( reader: &ProcessReader, address: usize, size: u32, -) -> Result<ThinVec<u8>, ReadError> { +) -> Result<ThinVec<u8>, process_reader::error::ReadError> { let value = reader.copy_array::<u8>(address, size as _)?; Ok(ThinVec::<u8>::from_iter(value.into_iter())) } diff --git a/toolkit/crashreporter/mozannotation_server/src/process_reader.rs b/toolkit/crashreporter/mozannotation_server/src/process_reader.rs deleted file mode 100644 index b405b4b725..0000000000 --- a/toolkit/crashreporter/mozannotation_server/src/process_reader.rs +++ /dev/null @@ -1,18 +0,0 @@ -/* 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 crate::ProcessHandle; - -pub struct ProcessReader { - process: ProcessHandle, -} - -#[cfg(target_os = "windows")] -mod windows; - -#[cfg(any(target_os = "android", target_os = "linux"))] -mod linux; - -#[cfg(target_os = "macos")] -mod macos; diff --git a/toolkit/crashreporter/mozannotation_server/src/process_reader/linux.rs b/toolkit/crashreporter/mozannotation_server/src/process_reader/linux.rs deleted file mode 100644 index 34f8ef90d5..0000000000 --- a/toolkit/crashreporter/mozannotation_server/src/process_reader/linux.rs +++ /dev/null @@ -1,314 +0,0 @@ -/* 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 mozannotation_client::MozAnnotationNote; -use std::{ - cmp::min, - fs::File, - io::{BufRead, BufReader, Error}, - mem::{size_of, MaybeUninit}, - ptr::null_mut, - slice, -}; - -use crate::{ - errors::{FindAnnotationsAddressError, PtraceError, ReadError, RetrievalError}, - ProcessHandle, -}; - -use super::ProcessReader; - -use goblin::elf::{ - self, - program_header::{PF_R, PT_NOTE}, - Elf, ProgramHeader, -}; -use libc::{ - c_int, c_long, c_void, pid_t, ptrace, waitpid, EINTR, PTRACE_ATTACH, PTRACE_DETACH, - PTRACE_PEEKDATA, __WALL, -}; -use memoffset::offset_of; -use mozannotation_client::ANNOTATION_TYPE; - -impl ProcessReader { - pub fn new(process: ProcessHandle) -> Result<ProcessReader, RetrievalError> { - let pid: pid_t = process; - - ptrace_attach(pid)?; - - let mut status: i32 = 0; - - loop { - let res = unsafe { waitpid(pid, &mut status as *mut _, __WALL) }; - if res < 0 { - match get_errno() { - EINTR => continue, - _ => { - ptrace_detach(pid)?; - return Err(RetrievalError::WaitPidError); - } - } - } else { - break; - } - } - - Ok(ProcessReader { process: pid }) - } - - pub fn find_annotations(&self) -> Result<usize, FindAnnotationsAddressError> { - let maps_file = File::open(format!("/proc/{}/maps", self.process))?; - - BufReader::new(maps_file) - .lines() - .flatten() - .find_map(|line| self.find_annotations_in_module(&line).ok()) - .ok_or(FindAnnotationsAddressError::NotFound) - } - - fn find_annotations_in_module(&self, line: &str) -> Result<usize, FindAnnotationsAddressError> { - parse_proc_maps_line(line).and_then(|module_address| { - let header_bytes = self.copy_array(module_address, size_of::<elf::Header>())?; - let elf_header = Elf::parse_header(&header_bytes)?; - - let program_header_bytes = self.copy_array( - module_address + (elf_header.e_phoff as usize), - (elf_header.e_phnum as usize) * (elf_header.e_phentsize as usize), - )?; - - let mut elf = Elf::lazy_parse(elf_header)?; - let context = goblin::container::Ctx { - container: elf.header.container()?, - le: elf.header.endianness()?, - }; - - elf.program_headers = ProgramHeader::parse( - &program_header_bytes, - 0, - elf_header.e_phnum as usize, - context, - )?; - - self.find_mozannotation_note(module_address, &elf) - .ok_or(FindAnnotationsAddressError::ProgramHeaderNotFound) - }) - } - - // Looks through the program headers for the note contained in the - // mozannotation_client crate. If the note is found return the address of the - // note's desc field as well as its contents. - fn find_mozannotation_note(&self, module_address: usize, elf: &Elf) -> Option<usize> { - for program_header in elf.program_headers.iter() { - // We're looking for a note in the program headers, it needs to be - // readable and it needs to be at least as large as the - // MozAnnotationNote structure. - if (program_header.p_type == PT_NOTE) - && ((program_header.p_flags & PF_R) != 0 - && (program_header.p_memsz as usize >= size_of::<MozAnnotationNote>())) - { - // Iterate over the notes - let notes_address = module_address + program_header.p_offset as usize; - let mut notes_offset = 0; - let notes_size = program_header.p_memsz as usize; - while notes_offset < notes_size { - let note_address = notes_address + notes_offset; - if let Ok(note) = self.copy_object::<goblin::elf::note::Nhdr32>(note_address) { - if note.n_type == ANNOTATION_TYPE { - if let Ok(note) = self.copy_object::<MozAnnotationNote>(note_address) { - let desc = note.desc; - let ehdr = (-note.ehdr) as usize; - let offset = desc + ehdr - - (offset_of!(MozAnnotationNote, ehdr) - - offset_of!(MozAnnotationNote, desc)); - - return usize::checked_add(module_address, offset); - } - } - - notes_offset += size_of::<goblin::elf::note::Nhdr32>() - + (note.n_descsz as usize) - + (note.n_namesz as usize); - } else { - break; - } - } - } - } - - None - } - - pub fn copy_object_shallow<T>(&self, src: usize) -> Result<MaybeUninit<T>, ReadError> { - let data = self.copy_array(src, size_of::<T>())?; - let mut object = MaybeUninit::<T>::uninit(); - let uninitialized_object = uninit_as_bytes_mut(&mut object); - - for (index, &value) in data.iter().enumerate() { - uninitialized_object[index].write(value); - } - - Ok(object) - } - - pub fn copy_object<T>(&self, src: usize) -> Result<T, ReadError> { - self.copy_object_shallow(src) - .map(|object| unsafe { object.assume_init() }) - } - - pub fn copy_array<T>(&self, src: usize, num: usize) -> Result<Vec<T>, ReadError> { - let mut array = Vec::<MaybeUninit<T>>::with_capacity(num); - let num_bytes = num * size_of::<T>(); - let mut array_buffer = array.as_mut_ptr() as *mut u8; - let mut index = 0; - - while index < num_bytes { - let word = ptrace_read(self.process, src + index)?; - let len = min(size_of::<c_long>(), num_bytes - index); - let word_as_bytes = word.to_ne_bytes(); - for &byte in word_as_bytes.iter().take(len) { - unsafe { - array_buffer.write(byte); - array_buffer = array_buffer.add(1); - } - } - - index += size_of::<c_long>(); - } - - unsafe { - array.set_len(num); - Ok(std::mem::transmute(array)) - } - } -} - -impl Drop for ProcessReader { - fn drop(&mut self) { - let _ignored = ptrace_detach(self.process); - } -} - -fn parse_proc_maps_line(line: &str) -> Result<usize, FindAnnotationsAddressError> { - let mut splits = line.trim().splitn(6, ' '); - let address_str = splits - .next() - .ok_or(FindAnnotationsAddressError::ProcMapsParseError)?; - let _perms_str = splits - .next() - .ok_or(FindAnnotationsAddressError::ProcMapsParseError)?; - let _offset_str = splits - .next() - .ok_or(FindAnnotationsAddressError::ProcMapsParseError)?; - let _dev_str = splits - .next() - .ok_or(FindAnnotationsAddressError::ProcMapsParseError)?; - let _inode_str = splits - .next() - .ok_or(FindAnnotationsAddressError::ProcMapsParseError)?; - let _path_str = splits - .next() - .ok_or(FindAnnotationsAddressError::ProcMapsParseError)?; - - let address = get_proc_maps_address(address_str)?; - - Ok(address) -} - -fn get_proc_maps_address(addresses: &str) -> Result<usize, FindAnnotationsAddressError> { - let begin = addresses - .split('-') - .next() - .ok_or(FindAnnotationsAddressError::ProcMapsParseError)?; - usize::from_str_radix(begin, 16).map_err(FindAnnotationsAddressError::from) -} - -fn uninit_as_bytes_mut<T>(elem: &mut MaybeUninit<T>) -> &mut [MaybeUninit<u8>] { - // SAFETY: MaybeUninit<u8> is always valid, even for padding bytes - unsafe { slice::from_raw_parts_mut(elem.as_mut_ptr() as *mut MaybeUninit<u8>, size_of::<T>()) } -} - -/*********************************************************************** - ***** libc helpers ***** - ***********************************************************************/ - -fn get_errno() -> c_int { - #[cfg(target_os = "linux")] - unsafe { - *libc::__errno_location() - } - #[cfg(target_os = "android")] - unsafe { - *libc::__errno() - } -} - -fn clear_errno() { - #[cfg(target_os = "linux")] - unsafe { - *libc::__errno_location() = 0; - } - #[cfg(target_os = "android")] - unsafe { - *libc::__errno() = 0; - } -} - -#[derive(Clone, Copy)] -enum PTraceOperation { - Attach, - Detach, - PeekData, -} - -#[cfg(all(target_os = "linux", target_env = "gnu"))] -type PTraceOperationNative = libc::c_uint; -#[cfg(all(target_os = "linux", target_env = "musl"))] -type PTraceOperationNative = libc::c_int; -#[cfg(target_os = "android")] -type PTraceOperationNative = c_int; - -impl From<PTraceOperation> for PTraceOperationNative { - fn from(val: PTraceOperation) -> Self { - match val { - PTraceOperation::Attach => PTRACE_ATTACH, - PTraceOperation::Detach => PTRACE_DETACH, - PTraceOperation::PeekData => PTRACE_PEEKDATA, - } - } -} - -fn ptrace_attach(pid: pid_t) -> Result<(), PtraceError> { - ptrace_helper(pid, PTraceOperation::Attach, 0).map(|_r| ()) -} - -fn ptrace_detach(pid: pid_t) -> Result<(), PtraceError> { - ptrace_helper(pid, PTraceOperation::Detach, 0).map(|_r| ()) -} - -fn ptrace_read(pid: libc::pid_t, addr: usize) -> Result<c_long, PtraceError> { - ptrace_helper(pid, PTraceOperation::PeekData, addr) -} - -fn ptrace_helper(pid: pid_t, op: PTraceOperation, addr: usize) -> Result<c_long, PtraceError> { - clear_errno(); - let result = unsafe { ptrace(op.into(), pid, addr, null_mut::<c_void>()) }; - - if result == -1 { - let errno = get_errno(); - if errno != 0 { - let error = match op { - PTraceOperation::Attach => PtraceError::TraceError(Error::from_raw_os_error(errno)), - PTraceOperation::Detach => PtraceError::TraceError(Error::from_raw_os_error(errno)), - PTraceOperation::PeekData => { - PtraceError::ReadError(Error::from_raw_os_error(errno)) - } - }; - Err(error) - } else { - Ok(result) - } - } else { - Ok(result) - } -} diff --git a/toolkit/crashreporter/mozannotation_server/src/process_reader/macos.rs b/toolkit/crashreporter/mozannotation_server/src/process_reader/macos.rs deleted file mode 100644 index 52a3957ca9..0000000000 --- a/toolkit/crashreporter/mozannotation_server/src/process_reader/macos.rs +++ /dev/null @@ -1,214 +0,0 @@ -/* 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 goblin::mach::{ - header::{Header64, MH_DYLIB, MH_EXECUTE, MH_MAGIC_64}, - load_command::{LoadCommandHeader, Section64, SegmentCommand64, LC_SEGMENT_64}, -}; -use mach2::{ - kern_return::KERN_SUCCESS, - task::task_info, - task_info::{task_dyld_info, TASK_DYLD_ALL_IMAGE_INFO_64, TASK_DYLD_INFO}, - vm::mach_vm_read_overwrite, -}; -use std::mem::{size_of, MaybeUninit}; - -use crate::{ - errors::{FindAnnotationsAddressError, ReadError, RetrievalError}, - ProcessHandle, -}; - -use super::ProcessReader; - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -struct AllImagesInfo { - // VERSION 1 - pub version: u32, - /// The number of [`ImageInfo`] structs at that following address - info_array_count: u32, - /// The address in the process where the array of [`ImageInfo`] structs is - info_array_addr: u64, - /// A function pointer, unused - _notification: u64, - /// Unused - _process_detached_from_shared_region: bool, - // VERSION 2 - lib_system_initialized: bool, - // Note that crashpad adds a 32-bit int here to get proper alignment when - // building on 32-bit targets...but we explicitly don't care about 32-bit - // targets since Apple doesn't - pub dyld_image_load_address: u64, -} - -/// `dyld_image_info` from <usr/include/mach-o/dyld_images.h> -#[repr(C)] -#[derive(Debug, Clone, Copy)] -struct ImageInfo { - /// The address in the process where the image is loaded - pub load_address: u64, - /// The address in the process where the image's file path can be read - pub file_path: u64, - /// Timestamp for when the image's file was last modified - pub file_mod_date: u64, -} - -const DATA_SEGMENT: &[u8; 16] = b"__DATA\0\0\0\0\0\0\0\0\0\0"; -const MOZANNOTATION_SECTION: &[u8; 16] = b"mozannotation\0\0\0"; - -impl ProcessReader { - pub fn new(process: ProcessHandle) -> Result<ProcessReader, RetrievalError> { - Ok(ProcessReader { process }) - } - - pub fn find_annotations(&self) -> Result<usize, FindAnnotationsAddressError> { - let dyld_info = self.task_info()?; - if (dyld_info.all_image_info_format as u32) != TASK_DYLD_ALL_IMAGE_INFO_64 { - return Err(FindAnnotationsAddressError::ImageFormatError); - } - - let all_image_info_size = dyld_info.all_image_info_size; - let all_image_info_addr = dyld_info.all_image_info_addr; - if (all_image_info_size as usize) < size_of::<AllImagesInfo>() { - return Err(FindAnnotationsAddressError::ImageFormatError); - } - - let all_images_info = self.copy_object::<AllImagesInfo>(all_image_info_addr as _)?; - - // Load the images - let images = self.copy_array::<ImageInfo>( - all_images_info.info_array_addr as _, - all_images_info.info_array_count as _, - )?; - - images - .iter() - .find_map(|image| self.find_annotations_in_image(image)) - .ok_or(FindAnnotationsAddressError::NotFound) - } - - fn task_info(&self) -> Result<task_dyld_info, FindAnnotationsAddressError> { - let mut info = std::mem::MaybeUninit::<task_dyld_info>::uninit(); - let mut count = (std::mem::size_of::<task_dyld_info>() / std::mem::size_of::<u32>()) as u32; - - let res = unsafe { - task_info( - self.process, - TASK_DYLD_INFO, - info.as_mut_ptr().cast(), - &mut count, - ) - }; - - if res == KERN_SUCCESS { - // SAFETY: this will be initialized if the call succeeded - unsafe { Ok(info.assume_init()) } - } else { - Err(FindAnnotationsAddressError::TaskInfoError) - } - } - - fn find_annotations_in_image(&self, image: &ImageInfo) -> Option<usize> { - self.copy_object::<Header64>(image.load_address as _) - .map_err(FindAnnotationsAddressError::from) - .and_then(|header| { - let image_address = image.load_address as usize; - let mut address = image_address + size_of::<Header64>(); - - if header.magic == MH_MAGIC_64 - && (header.filetype == MH_EXECUTE || header.filetype == MH_DYLIB) - { - let end_of_commands = address + (header.sizeofcmds as usize); - - while address < end_of_commands { - let command = self.copy_object::<LoadCommandHeader>(address)?; - - if command.cmd == LC_SEGMENT_64 { - if let Ok(offset) = self.find_annotations_in_segment(address) { - return image_address - .checked_add(offset) - .ok_or(FindAnnotationsAddressError::InvalidAddress); - } - } - - address += command.cmdsize as usize; - } - } - - Err(FindAnnotationsAddressError::NotFound) - }) - .ok() - } - - fn find_annotations_in_segment( - &self, - segment_address: usize, - ) -> Result<usize, FindAnnotationsAddressError> { - let segment = self.copy_object::<SegmentCommand64>(segment_address)?; - - if segment.segname.eq(DATA_SEGMENT) { - let sections_addr = segment_address + size_of::<SegmentCommand64>(); - let sections = self.copy_array::<Section64>(sections_addr, segment.nsects as usize)?; - for section in §ions { - if section.sectname.eq(MOZANNOTATION_SECTION) { - return Ok(section.offset as usize); - } - } - } - - Err(FindAnnotationsAddressError::InvalidAddress) - } - - pub fn copy_object_shallow<T>(&self, src: usize) -> Result<MaybeUninit<T>, ReadError> { - let mut object = MaybeUninit::<T>::uninit(); - let mut size: u64 = 0; - let res = unsafe { - mach_vm_read_overwrite( - self.process, - src as u64, - size_of::<T>() as u64, - object.as_mut_ptr() as _, - &mut size as _, - ) - }; - - if res == KERN_SUCCESS { - Ok(object) - } else { - Err(ReadError::MachError) - } - } - - 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 mut array: Vec<MaybeUninit<T>> = Vec::with_capacity(num); - let mut size: u64 = 0; - let res = unsafe { - mach_vm_read_overwrite( - self.process, - src as u64, - (num * size_of::<T>()) as u64, - array.as_mut_ptr() as _, - &mut size as _, - ) - }; - - if res == KERN_SUCCESS { - unsafe { - array.set_len(num); - Ok(std::mem::transmute(array)) - } - } else { - Err(ReadError::MachError) - } - } -} - -impl Drop for ProcessReader { - fn drop(&mut self) {} -} diff --git a/toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs b/toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs deleted file mode 100644 index ffdcd95937..0000000000 --- a/toolkit/crashreporter/mozannotation_server/src/process_reader/windows.rs +++ /dev/null @@ -1,193 +0,0 @@ -/* 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) - } - } -} |