use super::*;
#[test]
#[cfg_attr(miri, ignore = "spawns a subprocess")]
fn hardened_policy_detects_freelist_tamper() {
use mnemosyne_core::policy::HardenedPolicy;
use std::env;
use std::process::Command;
use std::string::String;
if env::var("RUN_HARDENED_FREELIST_TAMPER_ABORT_TEST").is_ok() {
let _guard = crate::local_alloc::TEST_LOCK
.lock()
.expect("local allocator test lock was poisoned");
let ptr1 = unsafe { thread_alloc::<HardenedPolicy, MemoryBackendWrapper>(16, 8) };
let ptr2 = unsafe { thread_alloc::<HardenedPolicy, MemoryBackendWrapper>(16, 8) };
assert!(!ptr1.is_null());
assert!(!ptr2.is_null());
unsafe {
thread_free::<HardenedPolicy, MemoryBackendWrapper>(ptr1);
thread_free::<HardenedPolicy, MemoryBackendWrapper>(ptr2);
}
let ptr_val = ptr1 as usize;
let segment_addr = ptr_val & !(SEGMENT_SIZE - 1);
let segment = segment_addr as *mut Segment;
let page_index = (ptr_val >> PAGE_SHIFT) & (PAGES_PER_SEGMENT - 1);
let page = unsafe { &raw mut (*segment).pages[page_index] };
let active_head = unsafe { (*page).free.or((*page).secondary_free) }
.expect("invariant: two frees onto one page leave a live head in one of its chains");
let raw_word = active_head.as_ptr() as *mut usize;
unsafe {
*raw_word ^= SEGMENT_SIZE;
}
let _ = unsafe { thread_alloc::<HardenedPolicy, MemoryBackendWrapper>(16, 8) };
return;
}
let current_exe = env::current_exe().expect("invariant: a running test binary has a path");
let output = Command::new(current_exe)
.arg("tests::corruption::hardened_policy_detects_freelist_tamper")
.arg("--exact")
.env("RUN_HARDENED_FREELIST_TAMPER_ABORT_TEST", "1")
.output()
.expect("invariant: re-executing this test binary succeeds");
if output.status.success() {
std::println!(
"Subprocess stdout:\n{}",
String::from_utf8_lossy(&output.stdout)
);
std::println!(
"Subprocess stderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
panic!("allocating through a tampered hardened free-list link must abort the process");
}
}
#[test]
#[cfg_attr(miri, ignore = "spawns a subprocess")]
fn test_large_alloc_metadata_corruption_aborts_process() {
use std::env;
use std::process::Command;
use std::string::String;
if env::var("RUN_LARGE_ALLOC_METADATA_CORRUPTION_ABORT_TEST").is_ok() {
unsafe {
let ptr = thread_alloc::<StandardPolicy, MemoryBackendWrapper>(65536, 8);
assert!(!ptr.is_null());
let metadata_slot = (ptr as *mut usize).sub(1);
metadata_slot.write(0x1337);
thread_free::<StandardPolicy, MemoryBackendWrapper>(ptr);
}
return;
}
let current_exe = env::current_exe().unwrap();
let output = Command::new(current_exe)
.arg("tests::corruption::test_large_alloc_metadata_corruption_aborts_process")
.arg("--exact")
.env("RUN_LARGE_ALLOC_METADATA_CORRUPTION_ABORT_TEST", "1")
.output()
.unwrap();
if output.status.success() {
std::println!(
"Subprocess stdout:\n{}",
String::from_utf8_lossy(&output.stdout)
);
std::println!(
"Subprocess stderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
panic!("Subprocess succeeded but was expected to abort!");
}
}
#[test]
#[cfg_attr(miri, ignore = "spawns a subprocess")]
fn test_large_alloc_segment_invariant_corruption_aborts_process() {
use std::env;
use std::process::Command;
use std::string::String;
if env::var("RUN_LARGE_ALLOC_SEGMENT_INVARIANT_CORRUPTION_ABORT_TEST").is_ok() {
unsafe {
let ptr = thread_alloc::<StandardPolicy, MemoryBackendWrapper>(65536, 8);
assert!(!ptr.is_null());
let segment_ptr = *((ptr as *mut *mut Segment).sub(1));
(*segment_ptr).raw_alloc_ptr = core::ptr::null_mut();
thread_free::<StandardPolicy, MemoryBackendWrapper>(ptr);
}
return;
}
let current_exe = env::current_exe().unwrap();
let output = Command::new(current_exe)
.arg("tests::corruption::test_large_alloc_segment_invariant_corruption_aborts_process")
.arg("--exact")
.env(
"RUN_LARGE_ALLOC_SEGMENT_INVARIANT_CORRUPTION_ABORT_TEST",
"1",
)
.output()
.unwrap();
if output.status.success() {
std::println!(
"Subprocess stdout:\n{}",
String::from_utf8_lossy(&output.stdout)
);
std::println!(
"Subprocess stderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
panic!("Subprocess succeeded but was expected to abort!");
}
}
#[test]
#[cfg_attr(miri, ignore = "spawns a subprocess")]
fn test_free_list_corruption_out_of_bounds_aborts_process() {
use std::env;
use std::process::Command;
use std::string::String;
if env::var("RUN_FREE_LIST_CORRUPTION_OUT_OF_BOUNDS_ABORT_TEST").is_ok() {
unsafe {
let ptr = thread_alloc::<StandardPolicy, MemoryBackendWrapper>(16, 8);
assert!(!ptr.is_null());
let ptr_val = ptr as usize;
let segment_addr = ptr_val & !(SEGMENT_SIZE - 1);
let segment = segment_addr as *mut Segment;
let page_index = (ptr_val >> PAGE_SHIFT) & (PAGES_PER_SEGMENT - 1);
thread_free::<StandardPolicy, MemoryBackendWrapper>(ptr);
let cookie = (*segment).keys[page_index];
let corrupt_block = ptr as *mut Block;
let bad_ptr = 0x12345678 as *mut Block;
(*corrupt_block).set_next::<StandardPolicy>(NonNull::new(bad_ptr), cookie);
let _ptr_new = thread_alloc::<StandardPolicy, MemoryBackendWrapper>(16, 8);
let _ptr_another = thread_alloc::<StandardPolicy, MemoryBackendWrapper>(16, 8);
}
return;
}
let current_exe = env::current_exe().unwrap();
let output = Command::new(current_exe)
.arg("tests::corruption::test_free_list_corruption_out_of_bounds_aborts_process")
.arg("--exact")
.env("RUN_FREE_LIST_CORRUPTION_OUT_OF_BOUNDS_ABORT_TEST", "1")
.output()
.unwrap();
if output.status.success() {
std::println!(
"Subprocess stdout:\n{}",
String::from_utf8_lossy(&output.stdout)
);
std::println!(
"Subprocess stderr:\n{}",
String::from_utf8_lossy(&output.stderr)
);
panic!("Subprocess succeeded but was expected to abort!");
}
}