macro_rules! rtprintpanic {
($($t:tt)*) => {
#[cfg(feature = "std")]
let _ = $crate::io::Write::write_fmt(&mut $crate::io::stderr(), format_args!($($t)*));
}
}
macro_rules! rtabort {
($($t:tt)*) => {
{
rtprintpanic!("fatal runtime error: {}\n", format_args!($($t)*));
origin::program::trap();
}
}
}
use core::ffi::c_void;
pub(crate) struct Handler {
data: *mut c_void,
}
impl Handler {
pub(crate) unsafe fn new() -> Handler {
make_handler()
}
fn null() -> Handler {
Handler {
data: core::ptr::null_mut(),
}
}
}
impl Drop for Handler {
fn drop(&mut self) {
unsafe {
drop_handler(self.data);
}
}
}
use core::{mem, ptr};
use origin::signal::{
sigaction, Sigaction, SigactionFlags, Siginfo, Signal, SIGSTKSZ, SIG_DFL, SS_DISABLE,
};
use rustix::mm::{mmap_anonymous, munmap, MapFlags, ProtFlags};
use rustix::runtime::kernel_sigaltstack;
use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
use rustix::param::page_size;
unsafe extern "C" fn signal_handler(signum: Signal, info: *mut Siginfo, _data: *mut c_void) {
let (stack_addr, _stack_size, guard_size) = origin::thread::stack(origin::thread::current());
let guard_end = stack_addr.addr();
let guard_start = guard_end - guard_size;
let addr = (*info)
.__bindgen_anon_1
.__bindgen_anon_1
._sifields
._sigfault
._addr
.addr();
if guard_start <= addr && addr < guard_end {
rtprintpanic!(
"\nthread '{}' has overflowed its stack\n",
rustix::thread::name()
.map(|c_str| alloc_crate::format!("{:?}", c_str))
.unwrap_or("<unknown>".into())
);
rtabort!("stack overflow");
} else {
let mut action: Sigaction = mem::zeroed();
action.sa_handler_kernel = SIG_DFL;
let _ = sigaction(signum, Some(action));
}
}
static MAIN_ALTSTACK: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut());
static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false);
pub unsafe fn init() {
for &signal in &[Signal::SEGV, Signal::BUS] {
let mut action = sigaction(signal, None).unwrap();
#[allow(unpredictable_function_pointer_comparisons)]
if action.sa_handler_kernel == SIG_DFL {
action.sa_flags = SigactionFlags::SIGINFO | SigactionFlags::ONSTACK;
action.sa_handler_kernel = Some(mem::transmute(
signal_handler as unsafe extern "C" fn(_, _, _),
));
let _ = sigaction(signal, Some(action));
NEED_ALTSTACK.store(true, Ordering::Relaxed);
}
}
let handler = make_handler();
MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
mem::forget(handler);
}
unsafe fn get_stackp() -> *mut c_void {
#[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux"))]
let flags = MapFlags::PRIVATE | MapFlags::STACK;
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux")))]
let flags = MapFlags::PRIVATE;
let stackp = match mmap_anonymous(
ptr::null_mut(),
SIGSTKSZ + page_size(),
ProtFlags::READ | ProtFlags::WRITE,
flags,
) {
Ok(stackp) => stackp,
Err(err) => panic!("failed to allocate an alternative stack: {}", err),
};
match rustix::mm::mprotect(stackp, page_size(), rustix::mm::MprotectFlags::empty()) {
Ok(guard_result) => guard_result,
Err(err) => panic!("failed to set up alternative stack guard page: {}", err),
};
stackp.add(page_size())
}
unsafe fn get_stack() -> rustix::runtime::Stack {
rustix::runtime::Stack {
ss_sp: get_stackp(),
ss_flags: 0,
ss_size: SIGSTKSZ as _,
}
}
unsafe fn make_handler() -> Handler {
if !NEED_ALTSTACK.load(Ordering::Relaxed) {
return Handler::null();
}
let mut stack = kernel_sigaltstack(None).unwrap();
if stack.ss_flags & SS_DISABLE != 0 {
stack = get_stack();
let _ = kernel_sigaltstack(Some(stack)).unwrap();
Handler {
data: stack.ss_sp as *mut c_void,
}
} else {
Handler::null()
}
}
unsafe fn drop_handler(data: *mut c_void) {
if !data.is_null() {
let stack = rustix::runtime::Stack {
ss_sp: ptr::null_mut(),
ss_flags: SS_DISABLE,
ss_size: SIGSTKSZ as _,
};
let _ = kernel_sigaltstack(Some(stack));
let _ = munmap(data.sub(page_size()), SIGSTKSZ + page_size());
}
}