Skip to content

Commit

Permalink
Add abort handling (#63)
Browse files Browse the repository at this point in the history
* Add handling of SIGABRT on windows

* Start removing windows-sys from crash-handler

* Fix raise_purecall on aarch64

* Replace windowss-sys with winapi

* Update minidump-writer/crash-context

* Ignore duplicate memoffset
Jake-Shadle authored Nov 17, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 9e5a38c commit 7b8700d
Showing 13 changed files with 48 additions and 64 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# add the below section to `.cargo/config.toml`
# [target.x86_64-pc-windows-msvc]
# linker = "lld-link"

[target.'cfg(all())']
rustflags = [
2 changes: 1 addition & 1 deletion crash-handler/Cargo.toml
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ debug-print = []
cfg-if = "1.0"
# Definition of a portable crash-context that can be shared between various
# crates
crash-context = { version = "0.5", path = "../crash-context" }
crash-context = "0.5"
# Wrapper around libc
libc = "0.2"
# Nicer sync primitives
11 changes: 0 additions & 11 deletions crash-handler/tests/abort.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
//! Abort is not catchable by a SEH, at least when done via the `__fastfail`
//! instrinsic which is used by eg. `std::process::abort()`
//!
//! > Critical failures that may have corrupted program state and stack beyond
//! > recovery cannot be handled by the regular exception handling facility.
//! > Use __fastfail to terminate the process using minimal overhead.
//!
//! See <https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail?view=msvc-170>
//! for more information
#![cfg(unix)]

mod shared;

#[test]
1 change: 1 addition & 0 deletions crash-handler/tests/shared.rs
Original file line number Diff line number Diff line change
@@ -68,6 +68,7 @@ pub fn handles_crash(flavor: SadnessFlavor) {
use ch::ExceptionCode;

let ec = match flavor {
SadnessFlavor::Abort => ExceptionCode::Abort,
SadnessFlavor::DivideByZero => ExceptionCode::Fpe,
SadnessFlavor::Illegal => ExceptionCode::Illegal,
SadnessFlavor::InvalidParameter => ExceptionCode::InvalidParameter,
4 changes: 4 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
@@ -31,6 +31,10 @@ skip = [
# The crate is in the repo, so we have the path, but it's also a crates.io
# dependency
{ name = "crash-context" },

# crossbeam-epoch uses an old version
{ name = "memoffset", version = "=0.6.5" },

# range-map uses an old version, and is unfortunately unmaintained and has
# not seen a release in 5 years
{ name = "num-traits", version = "=0.1.43" },
1 change: 0 additions & 1 deletion minidumper-test/crash-client/src/main.rs
Original file line number Diff line number Diff line change
@@ -73,7 +73,6 @@ fn real_main() -> anyhow::Result<()> {
Signal::Trap => {
sadness_generator::raise_trap();
}
#[cfg(unix)]
Signal::Abort => {
sadness_generator::raise_abort();
}
9 changes: 4 additions & 5 deletions minidumper-test/src/lib.rs
Original file line number Diff line number Diff line change
@@ -24,7 +24,6 @@ pub fn dump_test(signal: Signal, use_thread: bool, dump_path: Option<PathBuf>) {

#[derive(clap::ValueEnum, Clone, Copy)]
pub enum Signal {
#[cfg(unix)]
Abort,
#[cfg(unix)]
Bus,
@@ -46,7 +45,6 @@ use std::fmt;
impl fmt::Display for Signal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
#[cfg(unix)]
Self::Abort => "abort",
#[cfg(unix)]
Self::Bus => "bus",
@@ -319,7 +317,6 @@ pub fn assert_minidump(md_buf: &[u8], signal: Signal) {

match native_os {
Os::Linux => match signal {
#[cfg(unix)]
Signal::Abort => {
verify!(CrashReason::LinuxGeneral(
errors::ExceptionCodeLinux::SIGABRT,
@@ -374,6 +371,9 @@ pub fn assert_minidump(md_buf: &[u8], signal: Signal) {
}
},
Os::Windows => match signal {
Signal::Abort => {
assert_eq!(crash_reason, CrashReason::from_windows_code(0x40000015));
}
Signal::Fpe => {
verify!(CrashReason::WindowsGeneral(
errors::ExceptionCodeWindows::EXCEPTION_INT_DIVIDE_BY_ZERO
@@ -410,7 +410,7 @@ pub fn assert_minidump(md_buf: &[u8], signal: Signal) {
assert_eq!(crash_reason, CrashReason::from_windows_error(0xc000000d));
}
#[cfg(unix)]
Signal::Bus | Signal::Abort => {
Signal::Bus => {
unreachable!();
}
#[cfg(target_os = "macos")]
@@ -420,7 +420,6 @@ pub fn assert_minidump(md_buf: &[u8], signal: Signal) {
},
#[allow(clippy::match_same_arms)]
Os::MacOs => match signal {
#[cfg(unix)]
Signal::Abort => {
verify!(CrashReason::MacGeneral(
errors::ExceptionCodeMac::EXC_SOFTWARE,
11 changes: 0 additions & 11 deletions minidumper-test/tests/abort.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
//! Abort is not catchable by a SEH, at least when done via the __fastfail
//! instrinsic which is used by eg. `std::process::abort()`
//!
//! > Critical failures that may have corrupted program state and stack beyond
//! > recovery cannot be handled by the regular exception handling facility.
//! > Use __fastfail to terminate the process using minimal overhead.
//!
//! See <https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail?view=msvc-170>
//! for more information
#![cfg(unix)]

use minidumper_test::*;

#[test]
17 changes: 6 additions & 11 deletions minidumper/Cargo.toml
Original file line number Diff line number Diff line change
@@ -19,12 +19,12 @@ keywords = ["crash", "minidump", "ipc", "out-of-process"]
[dependencies]
# Nicer cfg handling
cfg-if = "1.0"
crash-context = "0.4"
crash-context = "0.5"
libc = "0.2"
# Basic log emitting
log = "0.4"
# Minidump writing
minidump-writer = "0.5"
minidump-writer = "0.7"
# Event loop
polling = "2.2"
# Nicer locking primitives
@@ -40,19 +40,14 @@ uds = "0.2.6"
# Nicer binary interop
scroll = "0.11"

[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
version = "0.42" # Keep aligned with parking_lot & minidump-writer & crash-handler
features = [
"Win32_Foundation", # Core types eg HANDLE
"Win32_Networking_WinSock", # Sockets
"Win32_System_IO", # Sockets
"Win32_System_Threading", # OpenProcess
]
# Bindings to Win32
[target.'cfg(target_os = "windows")'.dependencies.winapi]
version = "0.3"
features = ["handleapi", "winbase", "winsock2"]

[dev-dependencies]
# Diskwrite example
crash-handler = { path = "../crash-handler" }
pretty_env_logger = "0.4.0"
# uuid generation
uuid = { version = "1.0", features = ["v4"] }
backtrace = "0.3"
2 changes: 1 addition & 1 deletion minidumper/src/ipc.rs
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ cfg_if::cfg_if! {
/// The id of the thread in the client process in which the crash originated
thread_id: u32,
/// The top level exception code, also found in the `EXCEPTION_POINTERS.ExceptionRecord.ExceptionCode`
exception_code: i32,
exception_code: u32,
}
} else if #[cfg(target_os = "macos")] {
mod mac;
2 changes: 1 addition & 1 deletion minidumper/src/ipc/server.rs
Original file line number Diff line number Diff line change
@@ -272,7 +272,7 @@ impl Server {
// the client process, and inform the dump writer that they are pointers
// to a different process, as MiniDumpWriteDump will internally read
// the processes memory as needed
let exception_pointers = dump_request.exception_pointers as *const std::ffi::c_void;
let exception_pointers = dump_request.exception_pointers as *const crash_context::ffi::EXCEPTION_POINTERS;

let crash_ctx = crash_context::CrashContext {
exception_pointers,
27 changes: 16 additions & 11 deletions minidumper/src/ipc/windows.rs
Original file line number Diff line number Diff line change
@@ -8,11 +8,18 @@ use std::{
io,
os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket},
};
use windows_sys::Win32::{
Foundation::{self as found, HANDLE},
Networking::WinSock::{self as ws, SOCKADDR_UN as sockaddr_un},
use winapi::{
shared::ws2def as ws_def,
um::{handleapi::SetHandleInformation, winbase::HANDLE_FLAG_INHERIT, winsock2 as ws},
};

#[derive(Copy, Clone)]
#[repr(C)]
pub struct sockaddr_un {
pub sun_family: u16,
pub sun_path: [u8; 108],
}

pub(crate) fn init() {
static INIT: parking_lot::Once = parking_lot::Once::new();
INIT.call_once(|| {
@@ -43,7 +50,7 @@ impl UnixSocketAddr {
.as_bytes();

let mut sock_addr = sockaddr_un {
sun_family: ws::AF_UNIX,
sun_family: ws::PF_UNIX as _,
sun_path: [0u8; 108],
};

@@ -67,7 +74,7 @@ impl UnixSocketAddr {

#[inline]
fn from_parts(addr: sockaddr_un, len: i32) -> io::Result<Self> {
if addr.sun_family != ws::AF_UNIX {
if addr.sun_family != ws::PF_UNIX as _ {
Err(io::Error::new(
io::ErrorKind::InvalidData,
"socket address is not a unix domain socket",
@@ -83,7 +90,7 @@ struct Socket(ws::SOCKET);
impl Socket {
pub fn new() -> io::Result<Socket> {
// SAFETY: syscall
let socket = unsafe { ws::socket(ws::AF_UNIX as i32, ws::SOCK_STREAM as i32, 0) };
let socket = unsafe { ws::socket(ws::PF_UNIX as i32, ws::SOCK_STREAM as i32, 0) };

if socket == ws::INVALID_SOCKET {
Err(last_socket_error())
@@ -94,7 +101,7 @@ impl Socket {
}
}

fn accept(&self, storage: *mut ws::SOCKADDR, len: &mut i32) -> io::Result<Self> {
fn accept(&self, storage: *mut ws_def::SOCKADDR, len: &mut i32) -> io::Result<Self> {
// SAFETY: syscall
let socket = unsafe { ws::accept(self.0, storage, len) };

@@ -110,9 +117,7 @@ impl Socket {
#[inline]
fn set_no_inherit(&self) -> io::Result<()> {
// SAFETY: syscall
if unsafe { found::SetHandleInformation(self.0 as HANDLE, found::HANDLE_FLAG_INHERIT, 0) }
== 0
{
if unsafe { SetHandleInformation(self.0 as _, HANDLE_FLAG_INHERIT, 0) } == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
@@ -198,7 +203,7 @@ impl Socket {
let result = unsafe {
ws::WSASend(
self.as_raw_socket() as _,
bufs.as_ptr().cast::<ws::WSABUF>() as *mut _,
bufs.as_ptr().cast::<ws_def::WSABUF>() as *mut _,
length,
&mut nwritten,
0,
23 changes: 12 additions & 11 deletions sadness-generator/src/lib.rs
Original file line number Diff line number Diff line change
@@ -8,14 +8,12 @@ use std::arch::asm;
/// How you would like your sadness.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SadnessFlavor {
/// `SIGABRT` on Unix.
/// `SIGABRT`
///
/// This is not implemented on Windows as [`std::process::abort`], the
/// canonical way to abort processes in Rust, uses the [fastfail](
/// https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail?view=msvc-170)
/// Note that on Windows, [`std::process::abort`], the canonical way to
/// abort processes in Rust, uses the [fastfail](https://docs.microsoft.com/en-us/cpp/intrinsics/fastfail?view=msvc-170)
/// intrinsic, which neither raises a `SIGABRT` signal, nor issue a Windows
/// exception.
#[cfg(unix)]
/// exception. The method in this library always uses `libc::abort`
Abort,
/// * `SIGSEGV` on Linux
/// * `EXCEPTION_ACCESS_VIOLATION` on Windows
@@ -69,7 +67,6 @@ impl SadnessFlavor {
/// This is not safe. It intentionally crashes.
pub unsafe fn make_sad(self) -> ! {
match self {
#[cfg(unix)]
Self::Abort => raise_abort(),
Self::Segfault => raise_segfault(),
Self::DivideByZero => raise_floating_point_exception(),
@@ -109,10 +106,9 @@ impl SadnessFlavor {
///
/// # Safety
///
/// This is not safe. It intentionally crashes.
#[cfg(unix)]
/// This is not safe. It intentionally emits `SIGABRT`.
pub unsafe fn raise_abort() -> ! {
std::process::abort()
libc::abort()
}

/// This is the fixed address used to generate a segfault. It's possible that
@@ -373,7 +369,12 @@ pub unsafe fn raise_stack_overflow_in_non_rust_thread_longjmp() -> ! {
/// This is not safe. It intentionally crashes.
#[cfg(target_os = "windows")]
pub unsafe fn raise_purecall() -> ! {
asm!("call _purecall");
extern "C" {
fn _purecall() -> i32;
}

_purecall();

std::process::abort()
}

0 comments on commit 7b8700d

Please sign in to comment.