Skip to content

Commit

Permalink
Add heap corruption testing (#87)
Browse files Browse the repository at this point in the history
* Add support for testing the new vectored exception handling

* Oops

* Moar
  • Loading branch information
Jake-Shadle authored Jun 8, 2024
1 parent c61c72e commit 603dcbb
Show file tree
Hide file tree
Showing 11 changed files with 109 additions and 6 deletions.
3 changes: 2 additions & 1 deletion crash-handler/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::Error;
/// floats.
#[derive(Copy, Clone)]
#[repr(i32)]
//#[allow(overflowing_literals)]
#[allow(overflowing_literals)]
pub enum ExceptionCode {
Abort = 0x40000015, // STATUS_FATAL_APP_EXIT
Fpe = -1073741676, // EXCEPTION_INT_DIVIDE_BY_ZERO
Expand All @@ -23,6 +23,7 @@ pub enum ExceptionCode {
InvalidParameter = -1073741811, // STATUS_INVALID_PARAMETER
Purecall = -1073741787, // STATUS_NONCONTINUABLE_EXCEPTION
User = 0xcca11ed, // https://github.com/chromium/crashpad/blob/fca8871ca3fb721d3afab370ca790122f9333bfd/util/win/exception_codes.h#L32
HeapCorruption = 0xc0000374, // STATUS_HEAP_CORRUPTION
}

/// A Windows exception handler
Expand Down
2 changes: 1 addition & 1 deletion crash-handler/src/windows/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ pub(super) unsafe extern "system" fn handle_exception(
super::jmp::longjmp(_jump.0, _jump.1);
}

const STATUS_HEAP_CORRUPTION: u32 = 0xC0000374;
const STATUS_HEAP_CORRUPTION: u32 = 0xc0000374;

/// Called on the exception thread when an exception occurs.
/// Gets to act before other exception handlers.
Expand Down
8 changes: 8 additions & 0 deletions crash-handler/tests/heap_corruption.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![cfg(windows)]

mod shared;

#[test]
fn handles_heap_corruption() {
shared::handles_crash(shared::SadnessFlavor::HeapCorruption);
}
1 change: 1 addition & 0 deletions crash-handler/tests/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ pub fn handles_crash(flavor: SadnessFlavor) {
SadnessFlavor::Segfault => ExceptionCode::Segv,
SadnessFlavor::StackOverflow { .. }=> ExceptionCode::StackOverflow,
SadnessFlavor::Trap => ExceptionCode::Trap,
SadnessFlavor::HeapCorruption => ExceptionCode::HeapCorruption,
};

assert_eq!(
Expand Down
4 changes: 4 additions & 0 deletions minidumper-test/crash-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ fn real_main() -> anyhow::Result<()> {
Signal::InvalidParameter => {
sadness_generator::raise_invalid_parameter();
}
#[cfg(windows)]
Signal::HeapCorruption => {
sadness_generator::raise_heap_corruption();
}
#[cfg(target_os = "macos")]
Signal::Guard => {
sadness_generator::raise_guard_exception();
Expand Down
12 changes: 10 additions & 2 deletions minidumper-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pub enum Signal {
Purecall,
#[cfg(windows)]
InvalidParameter,
#[cfg(windows)]
HeapCorruption,
#[cfg(target_os = "macos")]
Guard,
}
Expand All @@ -58,6 +60,8 @@ impl fmt::Display for Signal {
Self::Purecall => "purecall",
#[cfg(windows)]
Self::InvalidParameter => "invalid-parameter",
#[cfg(windows)]
Self::HeapCorruption => "heap-corruption",
#[cfg(target_os = "macos")]
Self::Guard => "guard",
})
Expand Down Expand Up @@ -362,7 +366,7 @@ pub fn assert_minidump(md_buf: &[u8], signal: Signal) {
));
}
#[cfg(windows)]
Signal::Purecall | Signal::InvalidParameter => {
Signal::Purecall | Signal::InvalidParameter | Signal::HeapCorruption => {
unreachable!("windows only");
}
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -409,6 +413,10 @@ pub fn assert_minidump(md_buf: &[u8], signal: Signal) {
Signal::InvalidParameter => {
assert_eq!(crash_reason, CrashReason::from_windows_error(0xc000000d));
}
#[cfg(windows)]
Signal::HeapCorruption => {
assert_eq!(crash_reason, CrashReason::from_windows_error(0xc0000374));
}
#[cfg(unix)]
Signal::Bus => {
unreachable!();
Expand Down Expand Up @@ -515,7 +523,7 @@ pub fn assert_minidump(md_buf: &[u8], signal: Signal) {
}
}
#[cfg(windows)]
Signal::Purecall | Signal::InvalidParameter => {
Signal::Purecall | Signal::InvalidParameter | Signal::HeapCorruption => {
unreachable!("windows only");
}
},
Expand Down
5 changes: 5 additions & 0 deletions minidumper-test/tests/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,8 @@ fn invalid_param_simple() {
fn invalid_param_threaded() {
run_threaded_test(Signal::InvalidParameter);
}

#[test]
fn heap_corruption() {
run_threaded_test(Signal::HeapCorruption);
}
4 changes: 2 additions & 2 deletions sadness-generator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ homepage = "https://github.com/EmbarkStudios/crash-handling/tree/main/sadness-ge
# so we use simulation, the simulation is so good it's real!
categories = ["simulation"]
keywords = ["crash", "sadness", "signal", "exception"]
# We use `asm!`
rust-version = "1.62.0"
# We use `asm!` and rawdylib Windows bindings
rust-version = "1.71.0"

[dependencies]
libc = "0.2"
11 changes: 11 additions & 0 deletions sadness-generator/src/bindings.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
output = "win_bindings.rs"
binds = ["LoadLibraryA", "GetProcAddress", "GetProcessHeap", "HeapFree"]

[bind-mode]
mode = "minwin"

[bind-mode.config]
enum-style = "minwin"
fix-naming = true
use-rust-casing = true
linking-style = "raw-dylib"
35 changes: 35 additions & 0 deletions sadness-generator/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ pub enum SadnessFlavor {
/// exception
#[cfg(windows)]
InvalidParameter,
/// Raises a `STATUS_HEAP_CORRUPTION` exception by freeing an invalid pointer
#[cfg(windows)]
HeapCorruption,
/// Raises an `EXC_GUARD` exception on Macos by placing a guard on a
/// file descriptor then attempting to perform the operation that was guarded
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -96,6 +99,8 @@ impl SadnessFlavor {
Self::Purecall => raise_purecall(),
#[cfg(windows)]
Self::InvalidParameter => raise_invalid_parameter(),
#[cfg(windows)]
Self::HeapCorruption => raise_heap_corruption(),
#[cfg(target_os = "macos")]
Self::Guard => raise_guard_exception(),
}
Expand Down Expand Up @@ -393,6 +398,36 @@ pub unsafe fn raise_invalid_parameter() -> ! {
std::process::abort()
}

#[cfg(target_os = "windows")]
#[allow(dead_code)]
mod win_bindings;

/// [`SadnessFlavor::HeapCorruption`]
///
/// # Safety
///
/// This is not safe. It intentionally crashes.
#[cfg(target_os = "windows")]
pub unsafe fn raise_heap_corruption() -> ! {
use win_bindings::*;

let kernel32 = load_library_a(b"kernel32.dll\0".as_ptr());

if kernel32 != 0 {
let heap_free: Option<
unsafe extern "system" fn(HeapHandle, u32, *const core::ffi::c_void) -> Bool,
> = std::mem::transmute(get_proc_address(kernel32, b"HeapFree\0".as_ptr()));

let heap_free = heap_free.expect("failed to acquire HeapFree function");

let bad_pointer = 3 as *mut core::ffi::c_void;
heap_free(get_process_heap(), 0, bad_pointer);
} else {
panic!("Can't corrupt heap: failed to load kernel32");
}
std::process::abort()
}

/// The identifier used when guarding the file resource when raising
/// an `EXC_GUARD` exception via [`raise_guard_exception`]
#[cfg(target_os = "macos")]
Expand Down
30 changes: 30 additions & 0 deletions sadness-generator/src/win_bindings.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Bindings generated by `minwin` 0.1.0
#![allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
clippy::upper_case_acronyms
)]
#[link(name = "kernel32", kind = "raw-dylib")]
extern "system" {
#[link_name = "GetProcAddress"]
pub fn get_proc_address(module: Hmodule, proc_name: Pcstr) -> Farproc;
#[link_name = "GetProcessHeap"]
pub fn get_process_heap() -> HeapHandle;
#[link_name = "HeapFree"]
pub fn heap_free(
heap: HeapHandle,
flags: HeapFlags::Enum,
mem: *const ::core::ffi::c_void,
) -> Bool;
#[link_name = "LoadLibraryA"]
pub fn load_library_a(lib_file_name: Pcstr) -> Hmodule;
}
pub type Bool = i32;
pub type Farproc = ::core::option::Option<unsafe extern "system" fn() -> isize>;
pub mod HeapFlags {
pub type Enum = u32;
}
pub type HeapHandle = isize;
pub type Hmodule = isize;
pub type Pcstr = *const u8;

0 comments on commit 603dcbb

Please sign in to comment.