summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2019-11-19 16:57:54 +0100
committerDavid Lönnhager <david.l@mullvad.net>2019-11-21 11:11:54 +0100
commitb4b5fd35fe85ef75628c88bbe1064345c5c70d86 (patch)
tree72c985415071b624f82d4e1ebe6e6b4f3c62bbe3
parent2431762f3beae66fc264315f410b31918323f099 (diff)
downloadmullvadvpn-b4b5fd35fe85ef75628c88bbe1064345c5c70d86.tar.xz
mullvadvpn-b4b5fd35fe85ef75628c88bbe1064345c5c70d86.zip
Log unhandled SEH exceptions on Windows
-rw-r--r--mullvad-daemon/Cargo.toml2
-rw-r--r--mullvad-daemon/src/main.rs4
-rw-r--r--mullvad-daemon/src/windows_exception_logging.rs151
3 files changed, 156 insertions, 1 deletions
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index 5eeb095d93..f32a16f745 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -56,7 +56,7 @@ simple-signal = "1.1"
[target.'cfg(windows)'.dependencies]
ctrlc = "3.0"
windows-service = { git = "https://github.com/mullvad/windows-service-rs.git", rev = "48d755b0afbf259ba547d4defc3e9340d1436cf6" }
-winapi = "0.3"
+winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "libloaderapi", "synchapi", "tlhelp32", "winbase", "winerror", "winuser"] }
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index 26e463ef70..ea44a51656 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -17,6 +17,8 @@ mod cli;
mod shutdown;
#[cfg(windows)]
mod system_service;
+#[cfg(windows)]
+mod windows_exception_logging;
const DAEMON_LOG_FILENAME: &str = "daemon.log";
@@ -48,6 +50,8 @@ fn init_logging(config: &cli::Config) -> Result<Option<PathBuf>, String> {
)
.map_err(|e| e.display_chain_with_msg("Unable to initialize logger"))?;
log_panics::init();
+ #[cfg(windows)]
+ windows_exception_logging::enable();
version::log_version();
if let Some(ref log_dir) = log_dir {
info!("Logging to {}", log_dir.display());
diff --git a/mullvad-daemon/src/windows_exception_logging.rs b/mullvad-daemon/src/windows_exception_logging.rs
new file mode 100644
index 0000000000..7fb84f1b97
--- /dev/null
+++ b/mullvad-daemon/src/windows_exception_logging.rs
@@ -0,0 +1,151 @@
+use std::{ffi::CStr, mem, os::raw::c_char};
+
+use winapi::{
+ ctypes::c_void,
+ shared::{
+ minwindef::{BYTE, DWORD, FALSE},
+ winerror::ERROR_NO_MORE_FILES,
+ },
+ um::{
+ errhandlingapi::SetUnhandledExceptionFilter,
+ handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
+ tlhelp32::{
+ CreateToolhelp32Snapshot, Module32First, Module32Next, MODULEENTRY32, TH32CS_SNAPMODULE,
+ },
+ winnt::{EXCEPTION_POINTERS, EXCEPTION_RECORD, HANDLE, LONG},
+ },
+ vc::excpt::EXCEPTION_EXECUTE_HANDLER,
+};
+
+/// Enable logging of unhandled SEH exceptions.
+pub fn enable() {
+ unsafe { SetUnhandledExceptionFilter(Some(logging_exception_filter)) };
+}
+
+extern "system" fn logging_exception_filter(info: *mut EXCEPTION_POINTERS) -> LONG {
+ // TODO: output the error constant's name instead of its numeric value
+ // SAFETY: Windows gives us valid pointers
+ let info: &EXCEPTION_POINTERS = unsafe { &*info };
+ let record: &EXCEPTION_RECORD = unsafe { &*info.ExceptionRecord };
+
+ match find_address_module(record.ExceptionAddress) {
+ Some(mod_info) => log::error!(
+ "Unhandled exception at {:#x?} in {}: {:#x?}",
+ record.ExceptionAddress as usize - mod_info.base_address as usize,
+ mod_info.name,
+ record.ExceptionCode,
+ ),
+ None => log::error!(
+ "Unhandled exception at {:#x?}: {:#x?}",
+ record.ExceptionAddress,
+ record.ExceptionCode
+ ),
+ }
+
+ EXCEPTION_EXECUTE_HANDLER
+}
+
+/// Return module info for the current process and given memory address.
+fn find_address_module(address: *mut c_void) -> Option<ModuleInfo> {
+ let snap =
+ ProcessSnapshot::new(TH32CS_SNAPMODULE, 0).expect("could not create process snapshot");
+
+ for module in snap.modules() {
+ let module_end_address = unsafe { module.base_address.offset(module.size as isize) };
+ if (address as *const BYTE) >= module.base_address
+ && (address as *const BYTE) < module_end_address
+ {
+ return Some(module);
+ }
+ }
+
+ None
+}
+
+struct ModuleInfo {
+ name: String,
+ base_address: *const BYTE,
+ size: usize,
+}
+
+struct ProcessSnapshot {
+ handle: HANDLE,
+}
+
+impl ProcessSnapshot {
+ fn new(flags: DWORD, process_id: DWORD) -> std::io::Result<ProcessSnapshot> {
+ let snap = unsafe { CreateToolhelp32Snapshot(flags, process_id) };
+
+ if snap == INVALID_HANDLE_VALUE {
+ Err(std::io::Error::last_os_error())
+ } else {
+ Ok(ProcessSnapshot { handle: snap })
+ }
+ }
+
+ fn handle(&self) -> HANDLE {
+ self.handle
+ }
+
+ fn modules(&self) -> ProcessSnapshotModules<'_> {
+ let mut entry: MODULEENTRY32 = unsafe { mem::zeroed() };
+ entry.dwSize = mem::size_of::<MODULEENTRY32>() as u32;
+
+ ProcessSnapshotModules {
+ snapshot: self,
+ iter_started: false,
+ temp_entry: entry,
+ }
+ }
+}
+
+impl Drop for ProcessSnapshot {
+ fn drop(&mut self) {
+ unsafe {
+ CloseHandle(self.handle);
+ }
+ }
+}
+
+struct ProcessSnapshotModules<'a> {
+ snapshot: &'a ProcessSnapshot,
+ iter_started: bool,
+ temp_entry: MODULEENTRY32,
+}
+
+impl Iterator for ProcessSnapshotModules<'_> {
+ type Item = ModuleInfo;
+
+ fn next(&mut self) -> Option<ModuleInfo> {
+ if self.iter_started {
+ if unsafe { Module32Next(self.snapshot.handle(), &mut self.temp_entry) } == FALSE {
+ let last_error = std::io::Error::last_os_error();
+
+ return if last_error.raw_os_error().unwrap() as u32 == ERROR_NO_MORE_FILES {
+ None
+ } else {
+ panic!(
+ "Windows error during ProcessSnapshot iteration: {}",
+ last_error
+ )
+ };
+ }
+ } else {
+ if unsafe { Module32First(self.snapshot.handle(), &mut self.temp_entry) } == FALSE {
+ let last_error = std::io::Error::last_os_error();
+ panic!(
+ "Windows error during ProcessSnapshot iteration: {}",
+ last_error
+ );
+ }
+ self.iter_started = true;
+ }
+
+ let cstr = unsafe { CStr::from_ptr(&self.temp_entry.szModule[0] as *const c_char) };
+ Some(ModuleInfo {
+ name: cstr.to_string_lossy().into_owned(),
+ base_address: self.temp_entry.modBaseAddr,
+ size: self.temp_entry.modBaseSize as usize,
+ })
+ }
+}