summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--mullvad-daemon/src/exception_logging/win.rs77
1 files changed, 35 insertions, 42 deletions
diff --git a/mullvad-daemon/src/exception_logging/win.rs b/mullvad-daemon/src/exception_logging/win.rs
index 6ecf994d59..94f78018d7 100644
--- a/mullvad-daemon/src/exception_logging/win.rs
+++ b/mullvad-daemon/src/exception_logging/win.rs
@@ -7,15 +7,19 @@ use std::{
os::windows::io::AsRawHandle,
path::{Path, PathBuf},
ptr,
+ sync::atomic::{AtomicBool, Ordering},
};
use talpid_types::ErrorExt;
use talpid_windows::process::{ModuleEntry, ProcessSnapshot};
use winapi::vc::excpt::EXCEPTION_EXECUTE_HANDLER;
use windows_sys::Win32::{
- Foundation::{BOOL, HANDLE},
+ Foundation::HANDLE,
System::{
Diagnostics::{
- Debug::{SetUnhandledExceptionFilter, CONTEXT, EXCEPTION_POINTERS, EXCEPTION_RECORD},
+ Debug::{
+ MiniDumpNormal, MiniDumpWriteDump, SetUnhandledExceptionFilter, CONTEXT,
+ EXCEPTION_POINTERS, EXCEPTION_RECORD, MINIDUMP_EXCEPTION_INFORMATION,
+ },
ToolHelp::TH32CS_SNAPMODULE,
},
Threading::{GetCurrentProcess, GetCurrentProcessId, GetCurrentThreadId},
@@ -30,38 +34,6 @@ pub fn enable() {
unsafe { SetUnhandledExceptionFilter(Some(logging_exception_filter)) };
}
-#[repr(C)]
-#[allow(dead_code)]
-enum MINIDUMP_TYPE {
- MiniDumpNormal = 0,
- // Add missing values as needed
-}
-
-#[repr(C, packed)]
-#[derive(Clone, Copy, Debug)]
-#[allow(non_snake_case)]
-struct MINIDUMP_EXCEPTION_INFORMATION {
- ThreadId: u32,
- ExceptionPointers: *const EXCEPTION_POINTERS,
- ClientPointers: BOOL,
-}
-
-#[link(name = "dbghelp")]
-extern "system" {
- /// Store exception information, stack trace, etc. in a file.
- fn MiniDumpWriteDump(
- hProcess: HANDLE,
- ProcessId: u32,
- hFile: HANDLE,
- DumpType: MINIDUMP_TYPE,
- ExceptionParam: *const MINIDUMP_EXCEPTION_INFORMATION,
-
- // Add types as needed:
- UserStreamParam: *const c_void,
- CallbackParam: *const c_void,
- ) -> BOOL;
-}
-
#[derive(thiserror::Error, Debug)]
enum MinidumpError {
#[error("Failed to create mini dump file")]
@@ -72,7 +44,7 @@ enum MinidumpError {
fn generate_minidump(
dump_file: &Path,
- exception_pointers: &EXCEPTION_POINTERS,
+ exception_pointers: *const EXCEPTION_POINTERS,
) -> Result<(), MinidumpError> {
// Open/create dump file
let handle_rs = fs::OpenOptions::new()
@@ -90,16 +62,20 @@ fn generate_minidump(
let exception_parameters = MINIDUMP_EXCEPTION_INFORMATION {
ThreadId: thread_id,
- ExceptionPointers: exception_pointers,
+ // We may treat *const as *mut if we wish?
+ // https://internals.rust-lang.org/t/what-is-the-real-difference-between-const-t-and-mut-t-raw-pointers/6127/22
+ ExceptionPointers: exception_pointers as *mut EXCEPTION_POINTERS,
ClientPointers: 0,
};
+ // SAFETY: MiniDumpWriteDump is not thread-safe, so for this to be safe all threads need to be
+ // synchronized. logging_exception_filter takes precaution by adding a thread-safe reentrancy guard.
if unsafe {
MiniDumpWriteDump(
process,
process_id,
handle as HANDLE,
- MINIDUMP_TYPE::MiniDumpNormal,
+ MiniDumpNormal,
&exception_parameters,
ptr::null(),
ptr::null(),
@@ -154,10 +130,27 @@ fn exception_code_to_string(value: &EXCEPTION_RECORD) -> Option<Cow<'_, str>> {
}
}
-unsafe extern "system" fn logging_exception_filter(info: *const EXCEPTION_POINTERS) -> i32 {
- // SAFETY: Windows gives us valid pointers
- let info: &EXCEPTION_POINTERS = &*info;
+unsafe extern "system" fn logging_exception_filter(info_ptr: *const EXCEPTION_POINTERS) -> i32 {
+ // Guard against reentrancy, which can happen if this fault handler triggers another fault or
+ // if multiple threads would fail "at the same time".
+ // We have to take pre-cuation to synchronize all threads because the backing dbghelp Windows
+ // API is *not* thread safe: https://learn.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis#thread-safety.
+ // We implicitly use it through for example MiniDumpWriteDump.
+ static REENTRANCY_GUARD: AtomicBool = AtomicBool::new(false);
+ if REENTRANCY_GUARD.swap(true, Ordering::SeqCst) {
+ // We are already handling an error, so let someone else handle this error
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+
+ if info_ptr.is_null() || !info_ptr.is_aligned() {
+ // We can't properly handle this error, so let someone else handle this error
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+ // SAFETY: We've explicitly checked for null pointer and
+ // alignment. We assume that Windows gives us valid pointers, i.e. info points to valid data.
+ let info: &EXCEPTION_POINTERS = &*info_ptr;
let record: &EXCEPTION_RECORD = &*info.ExceptionRecord;
+ let context: &CONTEXT = &*info.ContextRecord;
// Generate minidump
let dump_path = match log_dir() {
@@ -170,7 +163,7 @@ unsafe extern "system" fn logging_exception_filter(info: *const EXCEPTION_POINTE
}
};
- match generate_minidump(&dump_path, info) {
+ match generate_minidump(&dump_path, info_ptr) {
Ok(()) => log::info!("Wrote Minidump to {}.", dump_path.to_string_lossy()),
Err(e) => {
log::error!(
@@ -181,7 +174,7 @@ unsafe extern "system" fn logging_exception_filter(info: *const EXCEPTION_POINTE
}
// Log exception information
- let context_info = get_context_info(&*info.ContextRecord);
+ let context_info = get_context_info(context);
let error_str = match exception_code_to_string(record) {
Some(errstr) => errstr,