//! mmap-based growable stack with a guard page below. //! //! Layout (low → high address): //! [ guard page (PROT_NONE) | stack region ] //! ^ top() — initial stack pointer //! //! Stacks grow downward. Overflow lands in the guard page → SIGSEGV. use std::io; pub struct Stack { /// Bottom of the entire mmap'd region (start of guard page). base: *mut u8, /// Total mmap'd size: guard_size + stack_size. total_size: usize, /// Usable stack size (excluding guard page). stack_size: usize, } // Stack owns its memory; safe to send across threads. unsafe impl Send for Stack {} impl Stack { /// Allocate a new stack. `stack_size` is the usable region; one page is /// added below as a guard page. Both are rounded up to the page size. pub fn new(stack_size: usize) -> io::Result { let page = page_size(); let stack_size = round_up(stack_size, page); let guard_size = page; let total_size = guard_size + stack_size; let base = unsafe { libc::mmap( std::ptr::null_mut(), total_size, libc::PROT_READ | libc::PROT_WRITE, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, 0, ) }; if base == libc::MAP_FAILED { return Err(io::Error::last_os_error()); } let base = base as *mut u8; let ret = unsafe { libc::mprotect(base as *mut libc::c_void, guard_size, libc::PROT_NONE) }; if ret != 0 { let err = io::Error::last_os_error(); unsafe { libc::munmap(base as *mut libc::c_void, total_size) }; return Err(err); } Ok(Self { base, total_size, stack_size }) } /// 16-byte-aligned top of the usable region. pub fn top(&self) -> *mut u8 { let raw_top = self.base as usize + self.total_size; (raw_top & !15) as *mut u8 } /// Pointer to the bottom of the usable region (just above the guard page). pub fn usable_base(&self) -> *mut u8 { unsafe { self.base.add(page_size()) } } pub fn stack_size(&self) -> usize { self.stack_size } } impl Drop for Stack { fn drop(&mut self) { unsafe { libc::munmap(self.base as *mut libc::c_void, self.total_size); } } } fn page_size() -> usize { unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize } } fn round_up(n: usize, align: usize) -> usize { (n + align - 1) & !(align - 1) }