From d7eee80109f63c2b9ca8057b7461d8753827f010 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Thu, 30 Mar 2023 09:05:49 +0200 Subject: [PATCH 1/6] Nuke winapi --- Cargo.toml | 12 +- src/bin/test.rs | 19 +- src/windows.rs | 4 +- src/windows/ffi.rs | 474 +++++++++++++++++++++++++++++---- src/windows/minidump_writer.rs | 29 +- 5 files changed, 457 insertions(+), 81 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee85b34f..d34bdd1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,16 +31,13 @@ nix = { version = "0.26", default-features = false, features = [ "user", ] } +[target.'cfg(target_os = "windows")'.dependencies] +bitflags = "2.0" + [target.'cfg(target_os = "macos")'.dependencies] # Binds some additional mac specifics not in libc mach2 = "0.4" -# Additional bindings to Windows specific APIs. Note we don't use windows-sys -# due to massive version churn -[target.'cfg(target_os = "windows")'.dependencies.winapi] -version = "0.3" -features = ["handleapi", "minwindef", "processthreadsapi", "winnt"] - [dev-dependencies] # Minidump-processor is async so we need an executor futures = { version = "0.3", features = ["executor"] } @@ -54,3 +51,6 @@ dump_syms = { version = "2.0.0", default-features = false } minidump-processor = { version = "0.15", default-features = false } similar-asserts = "1.2" uuid = "1.0" + +[patch.crates-io] +crash-context = { path = "../crash-handling/crash-context" } diff --git a/src/bin/test.rs b/src/bin/test.rs index accbc998..21555fdb 100644 --- a/src/bin/test.rs +++ b/src/bin/test.rs @@ -295,14 +295,17 @@ mod linux { #[cfg(target_os = "windows")] mod windows { - use minidump_writer::ffi::{ - GetCurrentProcessId, GetCurrentThread, GetCurrentThreadId, GetThreadContext, - EXCEPTION_POINTERS, EXCEPTION_RECORD, - }; - use super::*; use std::mem; + #[link(name = "kernel32")] + extern "system" { + pub fn GetCurrentProcessId() -> u32; + pub fn GetCurrentThreadId() -> u32; + pub fn GetCurrentThread() -> isize; + pub fn GetThreadContext(thread: isize, context: *mut crash_context::CONTEXT) -> i32; + } + #[inline(never)] pub(super) fn real_main(args: Vec) -> Result<()> { let exception_code = u32::from_str_radix(&args[0], 16).unwrap(); @@ -310,7 +313,7 @@ mod windows { // Generate the exception and communicate back where the exception pointers // are unsafe { - let mut exception_record: EXCEPTION_RECORD = mem::zeroed(); + let mut exception_record: crash_context::EXCEPTION_RECORD = mem::zeroed(); let mut exception_context = std::mem::MaybeUninit::uninit(); let pid = GetCurrentProcessId(); @@ -320,12 +323,12 @@ mod windows { let mut exception_context = exception_context.assume_init(); - let exception_ptrs = EXCEPTION_POINTERS { + let exception_ptrs = crash_context::EXCEPTION_POINTERS { ExceptionRecord: &mut exception_record, ContextRecord: &mut exception_context, }; - exception_record.ExceptionCode = exception_code; + exception_record.ExceptionCode = exception_code as _; let exc_ptr_addr = &exception_ptrs as *const _ as usize; diff --git a/src/windows.rs b/src/windows.rs index 1717ad03..34b72444 100644 --- a/src/windows.rs +++ b/src/windows.rs @@ -1,3 +1,5 @@ pub mod errors; -pub mod ffi; +mod ffi; pub mod minidump_writer; + +pub use ffi::MinidumpType; diff --git a/src/windows/ffi.rs b/src/windows/ffi.rs index 9841075c..cc919f57 100644 --- a/src/windows/ffi.rs +++ b/src/windows/ffi.rs @@ -7,51 +7,151 @@ //! cause crashes or bad data, so the [`crash_context::ffi::CONTEXT`] is used //! instead. See [#63](https://github.com/rust-minidump/minidump-writer/issues/63) -#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)] +#![allow( + non_snake_case, + non_camel_case_types, + non_upper_case_globals, + clippy::upper_case_acronyms +)] -pub use crash_context::ffi::{capture_context, CONTEXT, EXCEPTION_POINTERS, EXCEPTION_RECORD}; -pub use winapi::{ - shared::minwindef::BOOL, - um::{ - processthreadsapi::{ - GetCurrentProcess, GetCurrentProcessId, GetCurrentThread, GetCurrentThreadId, - OpenProcess, OpenThread, ResumeThread, SuspendThread, - }, - winnt::HANDLE, - }, -}; +pub use crash_context::{capture_context, CONTEXT, EXCEPTION_POINTERS, EXCEPTION_RECORD}; -pub type MINIDUMP_TYPE = u32; +pub type HANDLE = isize; +pub type BOOL = i32; +pub const FALSE: BOOL = 0; -pub const MiniDumpNormal: MINIDUMP_TYPE = 0u32; +pub type Hresult = i32; +pub const STATUS_NONCONTINUABLE_EXCEPTION: i32 = -1073741787; -pub type MINIDUMP_CALLBACK_ROUTINE = Option< - unsafe extern "system" fn( - callbackparam: *mut ::core::ffi::c_void, - callbackinput: *const MINIDUMP_CALLBACK_INPUT, - callbackoutput: *mut MINIDUMP_CALLBACK_OUTPUT, - ) -> BOOL, ->; +pub type PROCESS_ACCESS_RIGHTS = u32; +pub const PROCESS_ALL_ACCESS: PROCESS_ACCESS_RIGHTS = 2097151; -#[derive(Copy, Clone)] -#[repr(C, packed(4))] -pub struct MINIDUMP_CALLBACK_INPUT { - dummy: u32, +pub type THREAD_ACCESS_RIGHTS = u32; +pub const THREAD_SUSPEND_RESUME: THREAD_ACCESS_RIGHTS = 2; +pub const THREAD_GET_CONTEXT: THREAD_ACCESS_RIGHTS = 8; +pub const THREAD_QUERY_INFORMATION: THREAD_ACCESS_RIGHTS = 64; + +bitflags::bitflags! { + /// + #[derive(Copy, Clone, Debug)] + #[repr(transparent)] + pub struct MinidumpType: u32 { + /// Include just the information necessary to capture stack traces for all + /// existing threads in a process. + const Normal = 0; + /// Include the data sections from all loaded modules. + /// + /// This results in the inclusion of global variables, which can make + /// the minidump file significantly larger. + const WithDataSegs = 1 << 0; + /// Include all accessible memory in the process. + /// + /// The raw memory data is included at the end, so that the initial + /// structures can be mapped directly without the raw memory information. + /// This option can result in a very large file. + const WithFullMemory = 1 << 1; + /// Include high-level information about the operating system handles that + /// are active when the minidump is made. + const WithHandleData = 1 << 2; + /// Stack and backing store memory written to the minidump file should be + /// filtered to remove all but the pointer values necessary to reconstruct a + /// stack trace. + const FilterMemory = 1 << 3; + /// Stack and backing store memory should be scanned for pointer references + /// to modules in the module list. + /// + /// If a module is referenced by stack or backing store memory, the + /// [`MINIDUMP_CALLBACK_OUTPUT_0::ModuleWriteFlags`] field is set to + /// [`ModuleWriteFlags::ModuleReferencedByMemory`]. + const ScanMemory = 1 << 4; + /// Include information from the list of modules that were recently + /// unloaded, if this information is maintained by the operating system. + const WithUnloadedModules = 1 << 5; + /// Include pages with data referenced by locals or other stack memory. + /// This option can increase the size of the minidump file significantly. + const WithIndirectlyReferencedMemory = 1 << 6; + /// Filter module paths for information such as user names or important + /// directories. + /// + /// This option may prevent the system from locating the image file and + /// should be used only in special situations. + const FilterModulePaths = 1 << 7; + /// Include complete per-process and per-thread information from the + /// operating system. + const WithProcessThreadData = 1 << 8; + /// Scan the virtual address space for [`PAGE_READWRITE`](https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants) + /// memory to be included. + const WithPrivateReadWriteMemory = 1 << 9; + /// Reduce the data that is dumped by eliminating memory regions that + /// are not essential to meet criteria specified for the dump. + /// + /// This can avoid dumping memory that may contain data that is private + /// to the user. However, it is not a guarantee that no private information + /// will be present. + const WithoutOptionalData = 1 << 10; + /// Include memory region information. + /// + /// See [MINIDUMP_MEMORY_INFO_LIST](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_memory_info_list) + const WithFullMemoryInfo = 1 << 11; + /// Include thread state information. + /// + /// See [MINIDUMP_THREAD_INFO_LIST](https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/ns-minidumpapiset-minidump_thread_info_list) + const WithThreadInfo = 1 << 12; + /// Include all code and code-related sections from loaded modules to + /// capture executable content. + /// + /// For per-module control, use the [`ModuleWriteFlags::ModuleWriteCodeSegs`] + const WithCodeSegs = 1 << 13; + /// Turns off secondary auxiliary-supported memory gathering. + const WithoutAuxiliaryState = 1 << 14; + /// Requests that auxiliary data providers include their state in the + /// dump image; the state data that is included is provider dependent. + /// + /// This option can result in a large dump image. + const WithFullAuxiliaryState = 1 << 15; + /// Scans the virtual address space for [`PAGE_WRITECOPY`](https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants) memory to be included. + const WithPrivateWriteCopyMemory = 1 << 16; + /// If you specify [`MinidumpType::MiniDumpWithFullMemory`], the + /// `MiniDumpWriteDump` function will fail if the function cannot read + /// the memory regions; however, if you include + /// [`IgnoreInaccessibleMemory`], the `MiniDumpWriteDump` function will + /// ignore the memory read failures and continue to generate the dump. + /// + /// Note that the inaccessible memory regions are not included in the dump. + const IgnoreInaccessibleMemory = 1 << 17; + /// Adds security token related data. + /// + /// This will make the "!token" extension work when processing a user-mode dump. + const WithTokenInformation = 1 << 18; + /// Adds module header related data. + const WithModuleHeaders = 1 << 19; + /// Adds filter triage related data. + const FilterTriage = 1 << 20; + /// Adds AVX crash state context registers. + const WithAvxXStateContext = 1 << 21; + /// Adds Intel Processor Trace related data. + const WithIptTrace = 1 << 22; + /// Scans inaccessible partial memory pages. + const ScanInaccessiblePartialPages = 1 << 23; + /// Exclude all memory with the virtual protection attribute of [`PAGE_WRITECOMBINE`](https://learn.microsoft.com/en-us/windows/win32/memory/memory-protection-constants). + const FilterWriteCombinedMemory = 1 << 24; + } } -#[derive(Copy, Clone)] +pub type VS_FIXEDFILEINFO_FILE_FLAGS = u32; + #[repr(C, packed(4))] -pub struct MINIDUMP_CALLBACK_OUTPUT { - dummy: u32, +pub struct MINIDUMP_USER_STREAM { + pub Type: u32, + pub BufferSize: u32, + pub Buffer: *mut std::ffi::c_void, } -#[derive(Copy, Clone)] #[repr(C, packed(4))] -pub struct MINIDUMP_CALLBACK_INFORMATION { - pub CallbackRoutine: MINIDUMP_CALLBACK_ROUTINE, - pub CallbackParam: *mut ::core::ffi::c_void, +pub struct MINIDUMP_USER_STREAM_INFORMATION { + pub UserStreamCount: u32, + pub UserStreamArray: *mut MINIDUMP_USER_STREAM, } -#[derive(Copy, Clone)] #[repr(C, packed(4))] pub struct MINIDUMP_EXCEPTION_INFORMATION { pub ThreadId: u32, @@ -59,30 +159,306 @@ pub struct MINIDUMP_EXCEPTION_INFORMATION { pub ClientPointers: BOOL, } -#[derive(Copy, Clone)] +pub type VS_FIXEDFILEINFO_FILE_OS = i32; +pub type VS_FIXEDFILEINFO_FILE_TYPE = i32; +pub type VS_FIXEDFILEINFO_FILE_SUBTYPE = i32; + +#[repr(C)] +pub struct VS_FIXEDFILEINFO { + pub dwSignature: u32, + pub dwStrucVersion: u32, + pub dwFileVersionMS: u32, + pub dwFileVersionLS: u32, + pub dwProductVersionMS: u32, + pub dwProductVersionLS: u32, + pub dwFileFlagsMask: u32, + pub dwFileFlags: VS_FIXEDFILEINFO_FILE_FLAGS, + pub dwFileOS: VS_FIXEDFILEINFO_FILE_OS, + pub dwFileType: VS_FIXEDFILEINFO_FILE_TYPE, + pub dwFileSubtype: VS_FIXEDFILEINFO_FILE_SUBTYPE, + pub dwFileDateMS: u32, + pub dwFileDateLS: u32, +} +#[repr(C, packed(4))] +pub struct MINIDUMP_MODULE_CALLBACK { + pub FullPath: *mut u16, + pub BaseOfImage: u64, + pub SizeOfImage: u32, + pub CheckSum: u32, + pub TimeDateStamp: u32, + pub VersionInfo: VS_FIXEDFILEINFO, + pub CvRecord: *mut std::ffi::c_void, + pub SizeOfCvRecord: u32, + pub MiscRecord: *mut std::ffi::c_void, + pub SizeOfMiscRecord: u32, +} + #[repr(C, packed(4))] -pub struct MINIDUMP_USER_STREAM { +pub struct MINIDUMP_INCLUDE_THREAD_CALLBACK { + pub ThreadId: u32, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_INCLUDE_MODULE_CALLBACK { + pub BaseOfImage: u64, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_IO_CALLBACK { + pub Handle: HANDLE, + pub Offset: u64, + pub Buffer: *mut std::ffi::c_void, + pub BufferBytes: u32, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_READ_MEMORY_FAILURE_CALLBACK { + pub Offset: u64, + pub Bytes: u32, + pub FailureStatus: Hresult, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_VM_QUERY_CALLBACK { + pub Offset: u64, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_VM_PRE_READ_CALLBACK { + pub Offset: u64, + pub Buffer: *mut std::ffi::c_void, + pub Size: u32, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_VM_POST_READ_CALLBACK { + pub Offset: u64, + pub Buffer: *mut std::ffi::c_void, + pub Size: u32, + pub Completed: u32, + pub Status: Hresult, +} + +cfg_if::cfg_if! { + if #[cfg(target_arch = "x86")] { + #[repr(C)] + struct CALLBACK_CONTEXT([u8; 716]); + } else if #[cfg(target_arch = "x86_64")] { + + } else if #[cfg(target_arch = "aarch64")] { + #[repr(C)] + struct CALLBACK_CONTEXT([u8; 912]); + } +} + +/// Oof, so we have a problem with these structs, they are all packed(4), but +/// `CONTEXT` is aligned by either 4 (x86) or 16 (x86_64/aarch64)...which Rust +/// doesn't currently allow https://github.com/rust-lang/rust/issues/59154, so +/// we need to basically cheat with a big byte array until that issue is fixed (possibly never) +#[repr(C)] +pub struct CALLBACK_CONTEXT([u8; std::mem::size_of::()]); + +cfg_if::cfg_if! { + if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { + #[repr(C, packed(4))] + pub struct MINIDUMP_THREAD_CALLBACK { + pub ThreadId: u32, + pub ThreadHandle: HANDLE, + pub Context: CALLBACK_CONTEXT, + pub SizeOfContext: u32, + pub StackBase: u64, + pub StackEnd: u64, + } + + #[repr(C, packed(4))] + pub struct MINIDUMP_THREAD_EX_CALLBACK { + pub ThreadId: u32, + pub ThreadHandle: HANDLE, + pub Context: CALLBACK_CONTEXT, + pub SizeOfContext: u32, + pub StackBase: u64, + pub StackEnd: u64, + pub BackingStoreBase: u64, + pub BackingStoreEnd: u64, + } + } else if #[cfg(target_arch = "aarch64")] { + #[repr(C, packed(4))] + pub struct MINIDUMP_THREAD_CALLBACK { + pub ThreadId: u32, + pub ThreadHandle: HANDLE, + pub Pad: u32, + pub Context: CALLBACK_CONTEXT, + pub SizeOfContext: u32, + pub StackBase: u64, + pub StackEnd: u64, + } + + #[repr(C, packed(4))] + pub struct MINIDUMP_THREAD_EX_CALLBACK { + pub ThreadId: u32, + pub ThreadHandle: HANDLE, + pub Pad: u32, + pub Context: CALLBACK_CONTEXT, + pub SizeOfContext: u32, + pub StackBase: u64, + pub StackEnd: u64, + pub BackingStoreBase: u64, + pub BackingStoreEnd: u64, + } + } +} + +#[repr(C)] +pub union MINIDUMP_CALLBACK_INPUT_0 { + pub Status: Hresult, + pub Thread: std::mem::ManuallyDrop, + pub ThreadEx: std::mem::ManuallyDrop, + pub Module: std::mem::ManuallyDrop, + pub IncludeThread: std::mem::ManuallyDrop, + pub IncludeModule: std::mem::ManuallyDrop, + pub Io: std::mem::ManuallyDrop, + pub ReadMemoryFailure: std::mem::ManuallyDrop, + pub SecondaryFlags: u32, + pub VmQuery: std::mem::ManuallyDrop, + pub VmPreRead: std::mem::ManuallyDrop, + pub VmPostRead: std::mem::ManuallyDrop, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_CALLBACK_INPUT { + pub ProcessId: u32, + pub ProcessHandle: HANDLE, + pub CallbackType: u32, + pub Anonymous: MINIDUMP_CALLBACK_INPUT_0, +} + +pub type VIRTUAL_ALLOCATION_TYPE = u32; + +#[repr(C, packed(4))] +pub struct MINIDUMP_MEMORY_INFO { + pub BaseAddress: u64, + pub AllocationBase: u64, + pub AllocationProtect: u32, + __alignment1: u32, + pub RegionSize: u64, + pub State: VIRTUAL_ALLOCATION_TYPE, + pub Protect: u32, pub Type: u32, - pub BufferSize: u32, - pub Buffer: *mut ::core::ffi::c_void, + __alignment2: u32, } -#[derive(Copy, Clone)] #[repr(C, packed(4))] -pub struct MINIDUMP_USER_STREAM_INFORMATION { - pub UserStreamCount: u32, - pub UserStreamArray: *mut MINIDUMP_USER_STREAM, +pub struct MINIDUMP_CALLBACK_OUTPUT_0_0 { + pub MemoryBase: u64, + pub MemorySize: u32, +} + +#[repr(C)] +pub struct MINIDUMP_CALLBACK_OUTPUT_0_1 { + pub CheckCancel: BOOL, + pub Cancel: BOOL, +} + +#[repr(C)] +pub struct MINIDUMP_CALLBACK_OUTPUT_0_2 { + pub VmRegion: MINIDUMP_MEMORY_INFO, + pub Continue: BOOL, } +#[repr(C)] +pub struct MINIDUMP_CALLBACK_OUTPUT_0_3 { + pub VmQueryStatus: Hresult, + pub VmQueryResult: MINIDUMP_MEMORY_INFO, +} + +#[repr(C)] +pub struct MINIDUMP_CALLBACK_OUTPUT_0_4 { + pub VmReadStatus: Hresult, + pub VmReadBytesCompleted: u32, +} + +bitflags::bitflags! { + /// Identifies the type of module information that will be written to the + /// minidump file by the MiniDumpWriteDump function. + #[derive(Copy, Clone)] + #[repr(transparent)] + pub struct ModuleWriteFlags: u32 { + /// Only module information will be written to the minidump file. + const ModuleWriteModule = 0x0001; + const ModuleWriteDataSeg = 0x0002; + const ModuleWriteMiscRecord = 0x0004; + const ModuleWriteCvRecord = 0x0008; + const ModuleReferencedByMemory = 0x0010; + const ModuleWriteTlsData = 0x0020; + const ModuleWriteCodeSegs = 0x0040; + } +} + +#[repr(C)] +pub union MINIDUMP_CALLBACK_OUTPUT_0 { + pub ModuleWriteFlags: ModuleWriteFlags, + pub ThreadWriteFlags: u32, + pub SecondaryFlags: u32, + pub Anonymous1: std::mem::ManuallyDrop, + pub Anonymous2: std::mem::ManuallyDrop, + pub Handle: HANDLE, + pub Anonymous3: std::mem::ManuallyDrop, + pub Anonymous4: std::mem::ManuallyDrop, + pub Anonymous5: std::mem::ManuallyDrop, + pub Status: Hresult, +} + +#[repr(C, packed(4))] +pub struct MINIDUMP_CALLBACK_OUTPUT { + pub Anonymous: MINIDUMP_CALLBACK_OUTPUT_0, +} + +pub type MINIDUMP_CALLBACK_ROUTINE = Option< + unsafe extern "system" fn( + CallbackParam: *mut std::ffi::c_void, + CallbackInput: *const MINIDUMP_CALLBACK_INPUT, + CallbackOutput: *mut MINIDUMP_CALLBACK_OUTPUT, + ) -> BOOL, +>; + +#[repr(C, packed(4))] +pub struct MINIDUMP_CALLBACK_INFORMATION { + pub CallbackRoutine: MINIDUMP_CALLBACK_ROUTINE, + pub CallbackParam: *mut std::ffi::c_void, +} + +#[link(name = "kernel32")] +extern "system" { + pub fn CloseHandle(handle: HANDLE) -> BOOL; + pub fn GetCurrentProcess() -> HANDLE; + pub fn GetCurrentThreadId() -> u32; + pub fn OpenProcess( + desired_access: PROCESS_ACCESS_RIGHTS, + inherit_handle: BOOL, + process_id: u32, + ) -> HANDLE; + pub fn OpenThread( + desired_access: THREAD_ACCESS_RIGHTS, + inherit_handle: BOOL, + thread_id: u32, + ) -> HANDLE; + pub fn ResumeThread(thread: HANDLE) -> u32; + pub fn SuspendThread(thread: HANDLE) -> u32; + pub fn GetThreadContext(thread: HANDLE, context: *mut CONTEXT) -> BOOL; +} + +#[link(name = "dbghelp")] extern "system" { - pub fn GetThreadContext(hthread: HANDLE, lpcontext: *mut CONTEXT) -> BOOL; pub fn MiniDumpWriteDump( - hprocess: HANDLE, - processid: u32, - hfile: HANDLE, - dumptype: MINIDUMP_TYPE, - exceptionparam: *const MINIDUMP_EXCEPTION_INFORMATION, - userstreamparam: *const MINIDUMP_USER_STREAM_INFORMATION, - callbackparam: *const MINIDUMP_CALLBACK_INFORMATION, + process: HANDLE, + process_id: u32, + file: HANDLE, + dump_type: MinidumpType, + exception_param: *const MINIDUMP_EXCEPTION_INFORMATION, + user_stream_param: *const MINIDUMP_USER_STREAM_INFORMATION, + callback_param: *const MINIDUMP_CALLBACK_INFORMATION, ) -> BOOL; } + +// #[minwin] +// mod blah {} diff --git a/src/windows/minidump_writer.rs b/src/windows/minidump_writer.rs index e538907b..eeb8dd00 100644 --- a/src/windows/minidump_writer.rs +++ b/src/windows/minidump_writer.rs @@ -1,22 +1,17 @@ +#![allow(unsafe_code)] + use crate::windows::errors::Error; use crate::windows::ffi::{ - capture_context, GetCurrentProcess, GetCurrentThreadId, GetThreadContext, MiniDumpNormal, - MiniDumpWriteDump, OpenProcess, OpenThread, ResumeThread, SuspendThread, EXCEPTION_POINTERS, - HANDLE, MINIDUMP_EXCEPTION_INFORMATION, MINIDUMP_USER_STREAM, MINIDUMP_USER_STREAM_INFORMATION, + capture_context, CloseHandle, GetCurrentProcess, GetCurrentThreadId, GetThreadContext, + MiniDumpWriteDump, MinidumpType, OpenProcess, OpenThread, ResumeThread, SuspendThread, + EXCEPTION_POINTERS, EXCEPTION_RECORD, FALSE, HANDLE, MINIDUMP_EXCEPTION_INFORMATION, + MINIDUMP_USER_STREAM, MINIDUMP_USER_STREAM_INFORMATION, PROCESS_ALL_ACCESS, + STATUS_NONCONTINUABLE_EXCEPTION, THREAD_GET_CONTEXT, THREAD_QUERY_INFORMATION, + THREAD_SUSPEND_RESUME, }; use minidump_common::format::{BreakpadInfoValid, MINIDUMP_BREAKPAD_INFO, MINIDUMP_STREAM_TYPE}; use scroll::Pwrite; use std::os::windows::io::AsRawHandle; -use winapi::{ - shared::minwindef::FALSE, - um::{ - handleapi::CloseHandle, - winnt::{ - EXCEPTION_RECORD, PROCESS_ALL_ACCESS, STATUS_NONCONTINUABLE_EXCEPTION, - THREAD_GET_CONTEXT, THREAD_QUERY_INFORMATION, THREAD_SUSPEND_RESUME, - }, - }, -}; pub struct MinidumpWriter { /// Optional exception information @@ -29,7 +24,7 @@ pub struct MinidumpWriter { tid: u32, /// The exception code for the dump #[allow(dead_code)] - exception_code: u32, + exception_code: i32, /// Whether we are dumping the current process or not is_external_process: bool, } @@ -49,7 +44,7 @@ impl MinidumpWriter { /// function can also fail if `thread_id` is specified and we are unable to /// acquire the thread's context pub fn dump_local_context( - exception_code: Option, + exception_code: Option, thread_id: Option, destination: &mut std::fs::File, ) -> Result<(), Error> { @@ -76,7 +71,7 @@ impl MinidumpWriter { tid, // thread id ); - if thread_handle.is_null() { + if thread_handle == 0 { return Err(Error::ThreadOpen(std::io::Error::last_os_error())); } @@ -276,7 +271,7 @@ impl MinidumpWriter { validity: BreakpadInfoValid::DumpThreadId.bits() | BreakpadInfoValid::RequestingThreadId.bits(), dump_thread_id: self.tid, - // Safety: syscall + // SAFETY: syscall requesting_thread_id: unsafe { GetCurrentThreadId() }, }; From b1bdeafd7b6f58786c5873e557d791873899cd15 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Thu, 30 Mar 2023 09:07:47 +0200 Subject: [PATCH 2/6] Add ability to specify MinidumpType --- src/windows/minidump_writer.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/windows/minidump_writer.rs b/src/windows/minidump_writer.rs index eeb8dd00..70cc420e 100644 --- a/src/windows/minidump_writer.rs +++ b/src/windows/minidump_writer.rs @@ -46,6 +46,7 @@ impl MinidumpWriter { pub fn dump_local_context( exception_code: Option, thread_id: Option, + minidump_type: Option, destination: &mut std::fs::File, ) -> Result<(), Error> { let exception_code = exception_code.unwrap_or(STATUS_NONCONTINUABLE_EXCEPTION); @@ -129,7 +130,7 @@ impl MinidumpWriter { exception_code, }; - Self::dump_crash_context(cc, destination) + Self::dump_crash_context(cc, minidump_type, destination) } } @@ -149,6 +150,7 @@ impl MinidumpWriter { /// for the duration of this function call. pub fn dump_crash_context( crash_context: crash_context::CrashContext, + minidump_type: Option, destination: &mut std::fs::File, ) -> Result<(), Error> { let pid = crash_context.process_id; @@ -162,7 +164,7 @@ impl MinidumpWriter { pid, // pid ); - if proc.is_null() { + if proc == 0 { return Err(std::io::Error::last_os_error().into()); } @@ -203,11 +205,15 @@ impl MinidumpWriter { is_external_process, }; - mdw.dump(destination) + mdw.dump(minidump_type, destination) } /// Writes a minidump to the specified file - fn dump(mut self, destination: &mut std::fs::File) -> Result<(), Error> { + fn dump( + mut self, + minidump_type: Option, + destination: &mut std::fs::File, + ) -> Result<(), Error> { let exc_info = self.exc_info.take(); let mut user_streams = Vec::with_capacity(1); @@ -236,7 +242,7 @@ impl MinidumpWriter { self.crashing_process, // HANDLE to the process with the crash we want to capture self.pid, // process id destination.as_raw_handle() as HANDLE, // file to write the minidump to - MiniDumpNormal, // MINIDUMP_TYPE - we _might_ want to make this configurable + minidump_type.unwrap_or(MinidumpType::Normal), exc_info .as_ref() .map_or(std::ptr::null(), |ei| ei as *const _), // exceptionparam - the actual exception information From 79a8c921c5d3f6c7e5ef0340c01e8738dec917c3 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 3 Apr 2023 12:46:31 +0200 Subject: [PATCH 3/6] Update crash-context --- Cargo.toml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d34bdd1e..8729cc53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT" [dependencies] byteorder = "1.3.2" cfg-if = "1.0" -crash-context = "0.5" +crash-context = "0.6" memoffset = "0.8" minidump-common = "0.15" scroll = "0.11" @@ -51,6 +51,3 @@ dump_syms = { version = "2.0.0", default-features = false } minidump-processor = { version = "0.15", default-features = false } similar-asserts = "1.2" uuid = "1.0" - -[patch.crates-io] -crash-context = { path = "../crash-handling/crash-context" } From 330cabf8e44b6da9f883663c00c91d5b769b68ae Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 3 Apr 2023 12:50:10 +0200 Subject: [PATCH 4/6] Oops --- src/windows/ffi.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/windows/ffi.rs b/src/windows/ffi.rs index cc919f57..9c1a1984 100644 --- a/src/windows/ffi.rs +++ b/src/windows/ffi.rs @@ -239,18 +239,6 @@ pub struct MINIDUMP_VM_POST_READ_CALLBACK { pub Status: Hresult, } -cfg_if::cfg_if! { - if #[cfg(target_arch = "x86")] { - #[repr(C)] - struct CALLBACK_CONTEXT([u8; 716]); - } else if #[cfg(target_arch = "x86_64")] { - - } else if #[cfg(target_arch = "aarch64")] { - #[repr(C)] - struct CALLBACK_CONTEXT([u8; 912]); - } -} - /// Oof, so we have a problem with these structs, they are all packed(4), but /// `CONTEXT` is aligned by either 4 (x86) or 16 (x86_64/aarch64)...which Rust /// doesn't currently allow https://github.com/rust-lang/rust/issues/59154, so From 3cc83df6dab24dc59e1a44a730a371566689da1e Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 3 Apr 2023 12:56:06 +0200 Subject: [PATCH 5/6] Fix windows test --- src/windows/ffi.rs | 3 --- tests/windows_minidump_writer.rs | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/windows/ffi.rs b/src/windows/ffi.rs index 9c1a1984..933228f8 100644 --- a/src/windows/ffi.rs +++ b/src/windows/ffi.rs @@ -447,6 +447,3 @@ extern "system" { callback_param: *const MINIDUMP_CALLBACK_INFORMATION, ) -> BOOL; } - -// #[minwin] -// mod blah {} diff --git a/tests/windows_minidump_writer.rs b/tests/windows_minidump_writer.rs index 9a6efa65..aeccb942 100644 --- a/tests/windows_minidump_writer.rs +++ b/tests/windows_minidump_writer.rs @@ -8,10 +8,11 @@ use minidump_writer::minidump_writer::MinidumpWriter; mod common; use common::start_child_and_return; -use winapi::um::{minwinbase::EXCEPTION_ILLEGAL_INSTRUCTION, winnt::STATUS_INVALID_PARAMETER}; - +const EXCEPTION_ILLEGAL_INSTRUCTION: i32 = -1073741795; +const STATUS_INVALID_PARAMETER: i32 = -1073741811; +#[link(name = "kernel32")] extern "system" { - pub(crate) fn GetCurrentThreadId() -> u32; + fn GetCurrentThreadId() -> u32; } fn get_crash_reason<'a, T: std::ops::Deref + 'a>( From e974ba96194ef4f59b7fa6a68b849c806750e764 Mon Sep 17 00:00:00 2001 From: Jake Shadle Date: Mon, 3 Apr 2023 13:05:28 +0200 Subject: [PATCH 6/6] More fixup --- tests/windows_minidump_writer.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/windows_minidump_writer.rs b/tests/windows_minidump_writer.rs index aeccb942..1b2c1d4b 100644 --- a/tests/windows_minidump_writer.rs +++ b/tests/windows_minidump_writer.rs @@ -36,8 +36,13 @@ fn dump_current_process() { .tempfile() .unwrap(); - MinidumpWriter::dump_local_context(Some(STATUS_INVALID_PARAMETER), None, tmpfile.as_file_mut()) - .expect("failed to write minidump"); + MinidumpWriter::dump_local_context( + Some(STATUS_INVALID_PARAMETER), + None, + None, + tmpfile.as_file_mut(), + ) + .expect("failed to write minidump"); let md = Minidump::read_path(tmpfile.path()).expect("failed to read minidump"); @@ -84,6 +89,7 @@ fn dump_specific_thread() { MinidumpWriter::dump_local_context( Some(STATUS_INVALID_PARAMETER), Some(crashing_thread_id), + None, tmpfile.as_file_mut(), ) .expect("failed to write minidump"); @@ -141,6 +147,7 @@ fn dump_external_process() { (process_id, exception_pointers, thread_id, exception_code) }; + let exception_code = exception_code as i32; assert_eq!(exception_code, EXCEPTION_ILLEGAL_INSTRUCTION); let crash_context = crash_context::CrashContext { @@ -157,7 +164,7 @@ fn dump_external_process() { // SAFETY: We keep the process we are dumping alive until the minidump is written // and the test process keep the pointers it sent us alive until it is killed - MinidumpWriter::dump_crash_context(crash_context, tmpfile.as_file_mut()) + MinidumpWriter::dump_crash_context(crash_context, None, tmpfile.as_file_mut()) .expect("failed to write minidump"); child.kill().expect("failed to kill child");