Use a range to identify SIGSEGV in stack guards

Previously, the `guard::init()` and `guard::current()` functions were
returning a `usize` address representing the top of the stack guard,
respectively for the main thread and for spawned threads.  The `SIGSEGV`
handler on `unix` targets checked if a fault was within one page below
that address, if so reporting it as a stack overflow.

Now `unix` targets report a `Range<usize>` representing the guard
memory, so it can cover arbitrary guard sizes.  Non-`unix` targets which
always return `None` for guards now do so with `Option<!>`, so they
don't pay any overhead.

For `linux-gnu` in particular, the previous guard upper-bound was
`stackaddr + guardsize`, as the protected memory was *inside* the stack.
This was a glibc bug, and starting from 2.27 they are moving the guard
*past* the end of the stack.  However, there's no simple way for us to
know where the guard page actually lies, so now we declare it as the
whole range of `stackaddr ± guardsize`, and any fault therein will be
called a stack overflow.  This fixes #47863.
This commit is contained in:
Josh Stone
2018-01-31 11:41:29 -08:00
parent e2de8deb09
commit 55b54a999b
7 changed files with 89 additions and 64 deletions

View File

@@ -57,9 +57,6 @@ mod imp {
use sys_common::thread_info;
// This is initialized in init() and only read from after
static mut PAGE_SIZE: usize = 0;
#[cfg(any(target_os = "linux", target_os = "android"))]
unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize {
#[repr(C)]
@@ -102,12 +99,12 @@ mod imp {
_data: *mut libc::c_void) {
use sys_common::util::report_overflow;
let guard = thread_info::stack_guard().unwrap_or(0);
let guard = thread_info::stack_guard().unwrap_or(0..0);
let addr = siginfo_si_addr(info);
// If the faulting address is within the guard page, then we print a
// message saying so and abort.
if guard != 0 && guard - PAGE_SIZE <= addr && addr < guard {
if guard.start <= addr && addr < guard.end {
report_overflow();
rtabort!("stack overflow");
} else {
@@ -123,8 +120,6 @@ mod imp {
static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut();
pub unsafe fn init() {
PAGE_SIZE = ::sys::os::page_size();
let mut action: sigaction = mem::zeroed();
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
action.sa_sigaction = signal_handler as sighandler_t;