Dynamically size sigaltstk in rustc

rustc installs a signal stack that assumes that
MINSIGSTKSZ is a constant, unchanging value.
Newer hardware undermines that assumption greatly,
with register files larger than MINSIGSTKZ.
Properly handle this so that it is correct on
all supported Linux versions with all CPUs.
This commit is contained in:
Jubilee Young 2023-07-10 01:45:31 -07:00
parent 71f71a5397
commit 094cb1a9fb

View File

@ -1453,13 +1453,13 @@ extern "C" fn print_stack_trace(_: libc::c_int) {
/// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
/// process, print a stack trace and then exit. /// process, print a stack trace and then exit.
pub(super) fn install() { pub(super) fn install() {
use std::alloc::{alloc, Layout};
unsafe { unsafe {
const ALT_STACK_SIZE: usize = libc::MINSIGSTKSZ + 64 * 1024; let alt_stack_size: usize = min_sigstack_size() + 64 * 1024;
let mut alt_stack: libc::stack_t = std::mem::zeroed(); let mut alt_stack: libc::stack_t = std::mem::zeroed();
alt_stack.ss_sp = alt_stack.ss_sp = alloc(Layout::from_size_align(alt_stack_size, 1).unwrap()).cast();
std::alloc::alloc(std::alloc::Layout::from_size_align(ALT_STACK_SIZE, 1).unwrap()) alt_stack.ss_size = alt_stack_size;
as *mut libc::c_void;
alt_stack.ss_size = ALT_STACK_SIZE;
libc::sigaltstack(&alt_stack, std::ptr::null_mut()); libc::sigaltstack(&alt_stack, std::ptr::null_mut());
let mut sa: libc::sigaction = std::mem::zeroed(); let mut sa: libc::sigaction = std::mem::zeroed();
@ -1469,6 +1469,23 @@ pub(super) fn install() {
libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut()); libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
} }
} }
/// Modern kernels on modern hardware can have dynamic signal stack sizes.
#[cfg(any(target_os = "linux", target_os = "android"))]
fn min_sigstack_size() -> usize {
const AT_MINSIGSTKSZ: core::ffi::c_ulong = 51;
let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) };
// If getauxval couldn't find the entry, it returns 0,
// so take the higher of the "constant" and auxval.
// This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
libc::MINSIGSTKSZ.max(dynamic_sigstksz as _)
}
/// Not all OS support hardware where this is needed.
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn min_sigstack_size() -> usize {
libc::MINSIGSTKSZ
}
} }
#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))] #[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]