From f76872ee056b492e4b7bf11e9c41a8ae5ef30843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Thu, 27 Feb 2020 00:00:00 +0000 Subject: [PATCH 1/2] Use cfg_if to select between different stack_overflow handlers No functional changes intended. --- src/libstd/sys/unix/mod.rs | 17 +- src/libstd/sys/unix/stack_overflow.rs | 280 +++++++++----------- src/libstd/sys/unix/stack_overflow_empty.rs | 13 + 3 files changed, 149 insertions(+), 161 deletions(-) create mode 100644 src/libstd/sys/unix/stack_overflow_empty.rs diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 06876cb06149e..721c37560ac5c 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -63,7 +63,22 @@ pub mod pipe; pub mod process; pub mod rand; pub mod rwlock; -pub mod stack_overflow; +cfg_if::cfg_if! { + if #[cfg(any( + target_os = "linux", + target_os = "macos", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "solaris", + all(target_os = "netbsd", not(target_vendor = "rumprun")), + target_os = "openbsd" + ))] { + pub mod stack_overflow; + } else { + pub mod stack_overflow_empty; + pub use self::stack_overflow_empty as stack_overflow; + } +} pub mod stdio; pub mod thread; pub mod thread_local; diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs index 528fe321efbce..9d2be4f977e93 100644 --- a/src/libstd/sys/unix/stack_overflow.rs +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -1,9 +1,15 @@ #![cfg_attr(test, allow(dead_code))] -use self::imp::{drop_handler, make_handler}; +use crate::mem; +use crate::ptr; -pub use self::imp::cleanup; -pub use self::imp::init; +use libc::MAP_FAILED; +use libc::{mmap, munmap}; +use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; +use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE}; +use libc::{MAP_ANON, MAP_PRIVATE, PROT_READ, PROT_WRITE, SIGSEGV}; + +use crate::sys_common::thread_info; pub struct Handler { _data: *mut libc::c_void, @@ -23,183 +29,137 @@ impl Drop for Handler { } } -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "solaris", - all(target_os = "netbsd", not(target_vendor = "rumprun")), - target_os = "openbsd" -))] -mod imp { - use super::Handler; - use crate::mem; - use crate::ptr; - - use libc::MAP_FAILED; - use libc::{mmap, munmap}; - use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL}; - use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE}; - use libc::{MAP_ANON, MAP_PRIVATE, PROT_READ, PROT_WRITE, SIGSEGV}; - - use crate::sys_common::thread_info; - - #[cfg(any(target_os = "linux", target_os = "android"))] - unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { - #[repr(C)] - struct siginfo_t { - a: [libc::c_int; 3], // si_signo, si_errno, si_code - si_addr: *mut libc::c_void, - } - - (*(info as *const siginfo_t)).si_addr as usize +#[cfg(any(target_os = "linux", target_os = "android"))] +unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { + #[repr(C)] + struct siginfo_t { + a: [libc::c_int; 3], // si_signo, si_errno, si_code + si_addr: *mut libc::c_void, } - #[cfg(not(any(target_os = "linux", target_os = "android")))] - unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { - (*info).si_addr as usize - } - - // Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages - // (unmapped pages) at the end of every thread's stack, so if a thread ends - // up running into the guard page it'll trigger this handler. We want to - // detect these cases and print out a helpful error saying that the stack - // has overflowed. All other signals, however, should go back to what they - // were originally supposed to do. - // - // This handler currently exists purely to print an informative message - // whenever a thread overflows its stack. We then abort to exit and - // indicate a crash, but to avoid a misleading SIGSEGV that might lead - // users to believe that unsafe code has accessed an invalid pointer; the - // SIGSEGV encountered when overflowing the stack is expected and - // well-defined. - // - // If this is not a stack overflow, the handler un-registers itself and - // then returns (to allow the original signal to be delivered again). - // Returning from this kind of signal handler is technically not defined - // to work when reading the POSIX spec strictly, but in practice it turns - // out many large systems and all implementations allow returning from a - // signal handler to work. For a more detailed explanation see the - // comments on #26458. - unsafe extern "C" fn signal_handler( - signum: libc::c_int, - info: *mut libc::siginfo_t, - _data: *mut libc::c_void, - ) { - use crate::sys_common::util::report_overflow; - - 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.start <= addr && addr < guard.end { - report_overflow(); - rtabort!("stack overflow"); - } else { - // Unregister ourselves by reverting back to the default behavior. - let mut action: sigaction = mem::zeroed(); - action.sa_sigaction = SIG_DFL; - sigaction(signum, &action, ptr::null_mut()); - - // See comment above for why this function returns. - } - } + (*(info as *const siginfo_t)).si_addr as usize +} - static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut(); +#[cfg(not(any(target_os = "linux", target_os = "android")))] +unsafe fn siginfo_si_addr(info: *mut libc::siginfo_t) -> usize { + (*info).si_addr as usize +} - pub unsafe fn init() { +// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages +// (unmapped pages) at the end of every thread's stack, so if a thread ends +// up running into the guard page it'll trigger this handler. We want to +// detect these cases and print out a helpful error saying that the stack +// has overflowed. All other signals, however, should go back to what they +// were originally supposed to do. +// +// This handler currently exists purely to print an informative message +// whenever a thread overflows its stack. We then abort to exit and +// indicate a crash, but to avoid a misleading SIGSEGV that might lead +// users to believe that unsafe code has accessed an invalid pointer; the +// SIGSEGV encountered when overflowing the stack is expected and +// well-defined. +// +// If this is not a stack overflow, the handler un-registers itself and +// then returns (to allow the original signal to be delivered again). +// Returning from this kind of signal handler is technically not defined +// to work when reading the POSIX spec strictly, but in practice it turns +// out many large systems and all implementations allow returning from a +// signal handler to work. For a more detailed explanation see the +// comments on #26458. +unsafe extern "C" fn signal_handler( + signum: libc::c_int, + info: *mut libc::siginfo_t, + _data: *mut libc::c_void, +) { + use crate::sys_common::util::report_overflow; + + 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.start <= addr && addr < guard.end { + report_overflow(); + rtabort!("stack overflow"); + } else { + // Unregister ourselves by reverting back to the default behavior. let mut action: sigaction = mem::zeroed(); - action.sa_flags = SA_SIGINFO | SA_ONSTACK; - action.sa_sigaction = signal_handler as sighandler_t; - sigaction(SIGSEGV, &action, ptr::null_mut()); - sigaction(SIGBUS, &action, ptr::null_mut()); - - let handler = make_handler(); - MAIN_ALTSTACK = handler._data; - mem::forget(handler); - } + action.sa_sigaction = SIG_DFL; + sigaction(signum, &action, ptr::null_mut()); - pub unsafe fn cleanup() { - Handler { _data: MAIN_ALTSTACK }; + // See comment above for why this function returns. } +} - unsafe fn get_stackp() -> *mut libc::c_void { - let stackp = - mmap(ptr::null_mut(), SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - if stackp == MAP_FAILED { - panic!("failed to allocate an alternative stack"); - } - stackp - } +static mut MAIN_ALTSTACK: *mut libc::c_void = ptr::null_mut(); - #[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd", - target_os = "solaris" - ))] - unsafe fn get_stack() -> libc::stack_t { - libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ } - } +pub unsafe fn init() { + let mut action: sigaction = mem::zeroed(); + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_sigaction = signal_handler as sighandler_t; + sigaction(SIGSEGV, &action, ptr::null_mut()); + sigaction(SIGBUS, &action, ptr::null_mut()); - #[cfg(target_os = "dragonfly")] - unsafe fn get_stack() -> libc::stack_t { - libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ } - } + let handler = make_handler(); + MAIN_ALTSTACK = handler._data; + mem::forget(handler); +} - pub unsafe fn make_handler() -> Handler { - let mut stack = mem::zeroed(); - sigaltstack(ptr::null(), &mut stack); - // Configure alternate signal stack, if one is not already set. - if stack.ss_flags & SS_DISABLE != 0 { - stack = get_stack(); - sigaltstack(&stack, ptr::null_mut()); - Handler { _data: stack.ss_sp as *mut libc::c_void } - } else { - Handler { _data: ptr::null_mut() } - } - } +pub unsafe fn cleanup() { + Handler { _data: MAIN_ALTSTACK }; +} - pub unsafe fn drop_handler(handler: &mut Handler) { - if !handler._data.is_null() { - let stack = libc::stack_t { - ss_sp: ptr::null_mut(), - ss_flags: SS_DISABLE, - // Workaround for bug in macOS implementation of sigaltstack - // UNIX2003 which returns ENOMEM when disabling a stack while - // passing ss_size smaller than MINSIGSTKSZ. According to POSIX - // both ss_sp and ss_size should be ignored in this case. - ss_size: SIGSTKSZ, - }; - sigaltstack(&stack, ptr::null_mut()); - munmap(handler._data, SIGSTKSZ); - } +unsafe fn get_stackp() -> *mut libc::c_void { + let stackp = + mmap(ptr::null_mut(), SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if stackp == MAP_FAILED { + panic!("failed to allocate an alternative stack"); } + stackp } -#[cfg(not(any( +#[cfg(any( target_os = "linux", target_os = "macos", - target_os = "dragonfly", target_os = "freebsd", - target_os = "solaris", - all(target_os = "netbsd", not(target_vendor = "rumprun")), - target_os = "openbsd" -)))] -mod imp { - use crate::ptr; - - pub unsafe fn init() {} + target_os = "netbsd", + target_os = "openbsd", + target_os = "solaris" +))] +unsafe fn get_stack() -> libc::stack_t { + libc::stack_t { ss_sp: get_stackp(), ss_flags: 0, ss_size: SIGSTKSZ } +} - pub unsafe fn cleanup() {} +#[cfg(target_os = "dragonfly")] +unsafe fn get_stack() -> libc::stack_t { + libc::stack_t { ss_sp: get_stackp() as *mut i8, ss_flags: 0, ss_size: SIGSTKSZ } +} - pub unsafe fn make_handler() -> super::Handler { - super::Handler { _data: ptr::null_mut() } +unsafe fn make_handler() -> Handler { + let mut stack = mem::zeroed(); + sigaltstack(ptr::null(), &mut stack); + // Configure alternate signal stack, if one is not already set. + if stack.ss_flags & SS_DISABLE != 0 { + stack = get_stack(); + sigaltstack(&stack, ptr::null_mut()); + Handler { _data: stack.ss_sp as *mut libc::c_void } + } else { + Handler { _data: ptr::null_mut() } } +} - pub unsafe fn drop_handler(_handler: &mut super::Handler) {} +unsafe fn drop_handler(handler: &mut Handler) { + if !handler._data.is_null() { + let stack = libc::stack_t { + ss_sp: ptr::null_mut(), + ss_flags: SS_DISABLE, + // Workaround for bug in macOS implementation of sigaltstack + // UNIX2003 which returns ENOMEM when disabling a stack while + // passing ss_size smaller than MINSIGSTKSZ. According to POSIX + // both ss_sp and ss_size should be ignored in this case. + ss_size: SIGSTKSZ, + }; + sigaltstack(&stack, ptr::null_mut()); + munmap(handler._data, SIGSTKSZ); + } } diff --git a/src/libstd/sys/unix/stack_overflow_empty.rs b/src/libstd/sys/unix/stack_overflow_empty.rs new file mode 100644 index 0000000000000..e97831b2c2855 --- /dev/null +++ b/src/libstd/sys/unix/stack_overflow_empty.rs @@ -0,0 +1,13 @@ +#![cfg_attr(test, allow(dead_code))] + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() {} + +pub unsafe fn cleanup() {} From 5402b64abc3645a33183689bce1420b77a1c26dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 28 Feb 2020 00:00:00 +0000 Subject: [PATCH 2/2] Prefer signal handlers provided by a sanitizer runtime to those in std --- src/libstd/lib.rs | 1 + src/libstd/sys/unix/mod.rs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 7b3c702b929c0..7db2c14262af9 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -241,6 +241,7 @@ #![feature(atomic_mut_ptr)] #![feature(box_syntax)] #![feature(c_variadic)] +#![feature(cfg_sanitize)] #![feature(cfg_target_has_atomic)] #![feature(cfg_target_thread_local)] #![feature(char_error_internals)] diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 721c37560ac5c..cfc57d3c9b58b 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -64,7 +64,16 @@ pub mod process; pub mod rand; pub mod rwlock; cfg_if::cfg_if! { + // Prefer signal handlers provided by a sanitizer runtime. if #[cfg(any( + sanitize = "address", + sanitize = "leak", + sanitize = "memory", + sanitize = "thread" + ))] { + pub mod stack_overflow_empty; + pub use self::stack_overflow_empty as stack_overflow; + } else if #[cfg(any( target_os = "linux", target_os = "macos", target_os = "dragonfly",