Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a range to identify SIGSEGV in stack guards #47912

Merged
merged 2 commits into from
Feb 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/libstd/sys/cloudabi/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,11 @@ impl Drop for Thread {

#[cfg_attr(test, allow(dead_code))]
pub mod guard {
pub unsafe fn current() -> Option<usize> {
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> {
None
}
pub unsafe fn init() -> Option<usize> {
pub unsafe fn init() -> Option<Guard> {
None
}
}
Expand Down
5 changes: 3 additions & 2 deletions src/libstd/sys/redox/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ impl Thread {
}

pub mod guard {
pub unsafe fn current() -> Option<usize> { None }
pub unsafe fn init() -> Option<usize> { None }
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
}
9 changes: 2 additions & 7 deletions src/libstd/sys/unix/stack_overflow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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 {
Expand All @@ -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;
Expand Down
115 changes: 70 additions & 45 deletions src/libstd/sys/unix/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,10 @@ impl Drop for Thread {
not(target_os = "solaris")))]
#[cfg_attr(test, allow(dead_code))]
pub mod guard {
pub unsafe fn current() -> Option<usize> { None }
pub unsafe fn init() -> Option<usize> { None }
use ops::Range;
pub type Guard = Range<usize>;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
}


Expand All @@ -222,14 +224,43 @@ pub mod guard {
use libc;
use libc::mmap;
use libc::{PROT_NONE, MAP_PRIVATE, MAP_ANON, MAP_FAILED, MAP_FIXED};
use ops::Range;
use sys::os;

#[cfg(any(target_os = "macos",
target_os = "bitrig",
target_os = "openbsd",
target_os = "solaris"))]
// This is initialized in init() and only read from after
static mut PAGE_SIZE: usize = 0;

pub type Guard = Range<usize>;

#[cfg(target_os = "solaris")]
unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
let mut current_stack: libc::stack_t = ::mem::zeroed();
assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
Some(current_stack.ss_sp)
}

#[cfg(target_os = "macos")]
unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
current().map(|s| s as *mut libc::c_void)
let stackaddr = libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize -
libc::pthread_get_stacksize_np(libc::pthread_self());
Some(stackaddr as *mut libc::c_void)
}

#[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
unsafe fn get_stack_start() -> Option<*mut libc::c_void> {
let mut current_stack: libc::stack_t = ::mem::zeroed();
assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(),
&mut current_stack), 0);

let extra = if cfg!(target_os = "bitrig") {3} else {1} * PAGE_SIZE;
let stackaddr = if libc::pthread_main_np() == 1 {
// main thread
current_stack.ss_sp as usize - current_stack.ss_size + extra
} else {
// new thread
current_stack.ss_sp as usize - current_stack.ss_size
};
Some(stackaddr as *mut libc::c_void)
}

#[cfg(any(target_os = "android", target_os = "freebsd",
Expand All @@ -253,8 +284,9 @@ pub mod guard {
ret
}

pub unsafe fn init() -> Option<usize> {
let psize = os::page_size();
pub unsafe fn init() -> Option<Guard> {
PAGE_SIZE = os::page_size();

let mut stackaddr = get_stack_start()?;

// Ensure stackaddr is page aligned! A parent process might
Expand All @@ -263,9 +295,9 @@ pub mod guard {
// stackaddr < stackaddr + stacksize, so if stackaddr is not
// page-aligned, calculate the fix such that stackaddr <
// new_page_aligned_stackaddr < stackaddr + stacksize
let remainder = (stackaddr as usize) % psize;
let remainder = (stackaddr as usize) % PAGE_SIZE;
if remainder != 0 {
stackaddr = ((stackaddr as usize) + psize - remainder)
stackaddr = ((stackaddr as usize) + PAGE_SIZE - remainder)
as *mut libc::c_void;
}

Expand All @@ -280,60 +312,42 @@ pub mod guard {
// Instead, we'll just note where we expect rlimit to start
// faulting, so our handler can report "stack overflow", and
// trust that the kernel's own stack guard will work.
Some(stackaddr as usize)
let stackaddr = stackaddr as usize;
Some(stackaddr - PAGE_SIZE..stackaddr)
} else {
// Reallocate the last page of the stack.
// This ensures SIGBUS will be raised on
// stack overflow.
let result = mmap(stackaddr, psize, PROT_NONE,
let result = mmap(stackaddr, PAGE_SIZE, PROT_NONE,
MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0);

if result != stackaddr || result == MAP_FAILED {
panic!("failed to allocate a guard page");
}

let guardaddr = stackaddr as usize;
let offset = if cfg!(target_os = "freebsd") {
2
} else {
1
};

Some(stackaddr as usize + offset * psize)
Some(guardaddr..guardaddr + offset * PAGE_SIZE)
}
}

#[cfg(target_os = "solaris")]
pub unsafe fn current() -> Option<usize> {
let mut current_stack: libc::stack_t = ::mem::zeroed();
assert_eq!(libc::stack_getbounds(&mut current_stack), 0);
Some(current_stack.ss_sp as usize)
}

#[cfg(target_os = "macos")]
pub unsafe fn current() -> Option<usize> {
Some(libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize -
libc::pthread_get_stacksize_np(libc::pthread_self()))
}

#[cfg(any(target_os = "openbsd", target_os = "bitrig"))]
pub unsafe fn current() -> Option<usize> {
let mut current_stack: libc::stack_t = ::mem::zeroed();
assert_eq!(libc::pthread_stackseg_np(libc::pthread_self(),
&mut current_stack), 0);

let extra = if cfg!(target_os = "bitrig") {3} else {1} * os::page_size();
Some(if libc::pthread_main_np() == 1 {
// main thread
current_stack.ss_sp as usize - current_stack.ss_size + extra
} else {
// new thread
current_stack.ss_sp as usize - current_stack.ss_size
})
#[cfg(any(target_os = "macos",
target_os = "bitrig",
target_os = "openbsd",
target_os = "solaris"))]
pub unsafe fn current() -> Option<Guard> {
let stackaddr = get_stack_start()? as usize;
Some(stackaddr - PAGE_SIZE..stackaddr)
}

#[cfg(any(target_os = "android", target_os = "freebsd",
target_os = "linux", target_os = "netbsd", target_os = "l4re"))]
pub unsafe fn current() -> Option<usize> {
pub unsafe fn current() -> Option<Guard> {
let mut ret = None;
let mut attr: libc::pthread_attr_t = ::mem::zeroed();
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
Expand All @@ -352,12 +366,23 @@ pub mod guard {
assert_eq!(libc::pthread_attr_getstack(&attr, &mut stackaddr,
&mut size), 0);

let stackaddr = stackaddr as usize;
ret = if cfg!(target_os = "freebsd") {
Some(stackaddr as usize - guardsize)
// FIXME does freebsd really fault *below* the guard addr?
let guardaddr = stackaddr - guardsize;
Some(guardaddr - PAGE_SIZE..guardaddr)
} else if cfg!(target_os = "netbsd") {
Some(stackaddr as usize)
Some(stackaddr - guardsize..stackaddr)
} else if cfg!(all(target_os = "linux", target_env = "gnu")) {
// glibc used to include the guard area within the stack, as noted in the BUGS
// section of `man pthread_attr_getguardsize`. This has been corrected starting
// with glibc 2.27, and in some distro backports, so the guard is now placed at the
// end (below) the stack. There's no easy way for us to know which we have at
// runtime, so we'll just match any fault in the range right above or below the
// stack base to call that fault a stack overflow.
Some(stackaddr - guardsize..stackaddr + guardsize)
} else {
Some(stackaddr as usize + guardsize)
Some(stackaddr..stackaddr + guardsize)
};
}
assert_eq!(libc::pthread_attr_destroy(&mut attr), 0);
Expand Down
5 changes: 3 additions & 2 deletions src/libstd/sys/wasm/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl Thread {
}

pub mod guard {
pub unsafe fn current() -> Option<usize> { None }
pub unsafe fn init() -> Option<usize> { None }
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
}
5 changes: 3 additions & 2 deletions src/libstd/sys/windows/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ impl Thread {

#[cfg_attr(test, allow(dead_code))]
pub mod guard {
pub unsafe fn current() -> Option<usize> { None }
pub unsafe fn init() -> Option<usize> { None }
pub type Guard = !;
pub unsafe fn current() -> Option<Guard> { None }
pub unsafe fn init() -> Option<Guard> { None }
}
9 changes: 5 additions & 4 deletions src/libstd/sys_common/thread_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@
#![allow(dead_code)] // stack_guard isn't used right now on all platforms

use cell::RefCell;
use sys::thread::guard::Guard;
use thread::Thread;

struct ThreadInfo {
stack_guard: Option<usize>,
stack_guard: Option<Guard>,
thread: Thread,
}

Expand All @@ -38,11 +39,11 @@ pub fn current_thread() -> Option<Thread> {
ThreadInfo::with(|info| info.thread.clone())
}

pub fn stack_guard() -> Option<usize> {
ThreadInfo::with(|info| info.stack_guard).and_then(|o| o)
pub fn stack_guard() -> Option<Guard> {
ThreadInfo::with(|info| info.stack_guard.clone()).and_then(|o| o)
}

pub fn set(stack_guard: Option<usize>, thread: Thread) {
pub fn set(stack_guard: Option<Guard>, thread: Thread) {
THREAD_INFO.with(|c| assert!(c.borrow().is_none()));
THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{
stack_guard,
Expand Down
2 changes: 1 addition & 1 deletion src/test/codegen/stack-probes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// ignore-wasm
// ignore-emscripten
// ignore-windows
// no-system-llvm
// min-system-llvm-version 5.0
// compile-flags: -C no-prepopulate-passes

#![crate_type = "lib"]
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/stack-probes-lto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// ignore-emscripten no processes
// ignore-musl FIXME #31506
// ignore-pretty
// no-system-llvm
// min-system-llvm-version 5.0
// compile-flags: -C lto
// no-prefer-dynamic

Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/stack-probes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// ignore-cloudabi no processes
// ignore-emscripten no processes
// ignore-musl FIXME #31506
// no-system-llvm
// min-system-llvm-version 5.0

use std::mem;
use std::process::Command;
Expand Down
2 changes: 1 addition & 1 deletion src/tools/compiletest/src/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ impl EarlyProps {
.expect("Malformed llvm version directive");
// Ignore if using system LLVM and actual version
// is smaller the minimum required version
!(config.system_llvm && &actual_version[..] < min_version)
config.system_llvm && &actual_version[..] < min_version
} else {
false
}
Expand Down