//! Stack allocator tests. //! //! Covers allocation, alignment, read/write across the usable region, and //! (via subprocess) that the guard page actually SIGSEGVs. use smarm::stack::Stack; #[test] fn top_is_16_byte_aligned() { let s = Stack::new(64 * 1024).unwrap(); assert_eq!(s.top() as usize % 16, 0); } #[test] fn top_is_within_allocation() { let s = Stack::new(64 * 1024).unwrap(); let top = s.top() as usize; let base = s.usable_base() as usize; assert!(top > base); assert!(top <= base + s.stack_size()); } #[test] fn write_and_read_top_of_stack() { let s = Stack::new(64 * 1024).unwrap(); let sentinel: u64 = 0xDEAD_BEEF_CAFE_1234; unsafe { let ptr = s.top().sub(8) as *mut u64; ptr.write_volatile(sentinel); assert_eq!(ptr.read_volatile(), sentinel); } } #[test] fn write_and_read_bottom_of_usable_region() { let s = Stack::new(64 * 1024).unwrap(); let sentinel: u64 = 0x0102_0304_0506_0708; unsafe { let ptr = s.usable_base() as *mut u64; ptr.write_volatile(sentinel); assert_eq!(ptr.read_volatile(), sentinel); } } #[test] fn small_stack_allocates() { assert!(Stack::new(4096).is_ok()); } #[test] fn large_stack_allocates() { assert!(Stack::new(8 * 1024 * 1024).is_ok()); } #[test] fn stack_size_at_least_requested() { let s = Stack::new(64 * 1024).unwrap(); assert!(s.stack_size() >= 64 * 1024); } // --------------------------------------------------------------------------- // Guard page SIGSEGV tests — subprocess-based. // --------------------------------------------------------------------------- use std::env; use std::process::Command; fn run_as_child_if_requested() { match env::var("SMARM_SUBTEST").as_deref() { Ok("guard_page_direct") => { let s = Stack::new(64 * 1024).unwrap(); unsafe { let guard_ptr = s.usable_base().sub(1); guard_ptr.write_volatile(0xAB); } std::process::exit(0); } Ok("stack_overflow") => { let s = Stack::new(64 * 1024).unwrap(); unsafe { let mut ptr = s.top().sub(1); let stop = s.usable_base().sub(1); while ptr >= stop { ptr.write_volatile(0xFF); ptr = ptr.sub(1); } } std::process::exit(0); } _ => {} } } fn spawn_subtest(name: &str) -> std::process::ExitStatus { let exe = env::current_exe().unwrap(); Command::new(exe) .env("SMARM_SUBTEST", name) .args(["--test-threads=1", "--quiet"]) .status() .expect("failed to spawn subprocess") } #[test] fn guard_page_causes_sigsegv() { run_as_child_if_requested(); let status = spawn_subtest("guard_page_direct"); #[cfg(unix)] { use std::os::unix::process::ExitStatusExt; assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status); } } #[test] fn stack_overflow_causes_sigsegv() { run_as_child_if_requested(); let status = spawn_subtest("stack_overflow"); #[cfg(unix)] { use std::os::unix::process::ExitStatusExt; assert_eq!(status.signal(), Some(11), "expected SIGSEGV, got: {:?}", status); } }