Skip to content

Commit

Permalink
auto merge of #16388 : Zoxc/rust/stmesg, r=alexcrichton
Browse files Browse the repository at this point in the history
This installs signal handlers to print out stack overflow messages on Linux. It also ensures the main thread has a guard page.

This will catch stack overflows in external code. It's done in preparation of switching to stack probes (#16012).

I've done some simple tests with overflowing the main thread, native threads and green threads (with and without UV) on x86-64.
This might work on ARM, MIPS and x86-32.

I've been unable to run the test suite on this because of #16305.
  • Loading branch information
bors committed Oct 24, 2014
2 parents 083578d + 70cef94 commit a10917a
Show file tree
Hide file tree
Showing 15 changed files with 774 additions and 43 deletions.
1 change: 1 addition & 0 deletions src/libgreen/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ impl Runtime for SimpleTask {
}
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> { None }
fn stack_bounds(&self) -> (uint, uint) { fail!() }
fn stack_guard(&self) -> Option<uint> { fail!() }
fn can_block(&self) -> bool { true }
fn wrap(self: Box<SimpleTask>) -> Box<Any+'static> { fail!() }
}
Expand Down
5 changes: 5 additions & 0 deletions src/libgreen/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ impl Stack {
}
}

/// Point to the last writable byte of the stack
pub fn guard(&self) -> *const uint {
(self.start() as uint + page_size()) as *const uint
}

/// Point to the low end of the allocated stack
pub fn start(&self) -> *const uint {
self.buf.as_ref().map(|m| m.data() as *const uint)
Expand Down
7 changes: 7 additions & 0 deletions src/libgreen/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,13 @@ impl Runtime for GreenTask {
c.current_stack_segment.end() as uint)
}

fn stack_guard(&self) -> Option<uint> {
let c = self.coroutine.as_ref()
.expect("GreenTask.stack_guard called without a coroutine");

Some(c.current_stack_segment.guard() as uint)
}

fn can_block(&self) -> bool { false }

fn wrap(self: Box<GreenTask>) -> Box<Any+'static> {
Expand Down
10 changes: 0 additions & 10 deletions src/libnative/io/c_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,16 +252,6 @@ mod signal {
pub status: libc::c_int,
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
#[repr(C)]
pub struct sigaction {
pub sa_handler: extern fn(libc::c_int),
sa_tramp: *mut libc::c_void,
pub sa_mask: sigset_t,
pub sa_flags: libc::c_int,
}

#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
#[repr(C)]
pub struct sigaction {
pub sa_handler: extern fn(libc::c_int),
Expand Down
3 changes: 2 additions & 1 deletion src/libnative/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ pub fn start(argc: int, argv: *const *const u8, main: proc()) -> int {
rt::init(argc, argv);
let mut exit_code = None;
let mut main = Some(main);
let mut task = task::new((my_stack_bottom, my_stack_top));
let mut task = task::new((my_stack_bottom, my_stack_top),
rt::thread::main_guard_page());
task.name = Some(str::Slice("<main>"));
drop(task.run(|| {
unsafe {
Expand Down
15 changes: 14 additions & 1 deletion src/libnative/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ use io;
use std::task::{TaskBuilder, Spawner};

/// Creates a new Task which is ready to execute as a 1:1 task.
pub fn new(stack_bounds: (uint, uint)) -> Box<Task> {
pub fn new(stack_bounds: (uint, uint), stack_guard: uint) -> Box<Task> {
let mut task = box Task::new();
let mut ops = ops();
ops.stack_bounds = stack_bounds;
ops.stack_guard = stack_guard;
task.put_runtime(ops);
return task;
}
Expand All @@ -44,6 +45,7 @@ fn ops() -> Box<Ops> {
io: io::IoFactory::new(),
// these *should* get overwritten
stack_bounds: (0, 0),
stack_guard: 0
}
}

Expand Down Expand Up @@ -82,6 +84,7 @@ impl Spawner for NativeSpawner {
my_stack);
}
let mut ops = ops;
ops.stack_guard = rt::thread::current_guard_page();
ops.stack_bounds = (my_stack - stack + 1024, my_stack);

let mut f = Some(f);
Expand Down Expand Up @@ -115,6 +118,8 @@ struct Ops {
// native tasks necessarily know their precise bounds, hence this is
// optional.
stack_bounds: (uint, uint),

stack_guard: uint
}

impl rt::Runtime for Ops {
Expand All @@ -138,6 +143,14 @@ impl rt::Runtime for Ops {

fn stack_bounds(&self) -> (uint, uint) { self.stack_bounds }

fn stack_guard(&self) -> Option<uint> {
if self.stack_guard != 0 {
Some(self.stack_guard)
} else {
None
}
}

fn can_block(&self) -> bool { true }

// This function gets a little interesting. There are a few safety and
Expand Down
5 changes: 5 additions & 0 deletions src/librustrt/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod local_ptr;
mod thread_local_storage;
mod util;
mod libunwind;
mod stack_overflow;

pub mod args;
pub mod bookkeeping;
Expand Down Expand Up @@ -92,6 +93,8 @@ pub trait Runtime {
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
/// The (low, high) edges of the current stack.
fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
/// The last writable byte of the stack next to the guard page
fn stack_guard(&self) -> Option<uint>;
fn can_block(&self) -> bool;

// FIXME: This is a serious code smell and this should not exist at all.
Expand All @@ -113,6 +116,7 @@ pub fn init(argc: int, argv: *const *const u8) {
args::init(argc, argv);
local_ptr::init();
at_exit_imp::init();
thread::init();
}

// FIXME(#14344) this shouldn't be necessary
Expand Down Expand Up @@ -151,6 +155,7 @@ pub unsafe fn cleanup() {
bookkeeping::wait_for_other_tasks();
at_exit_imp::run();
args::cleanup();
thread::cleanup();
local_ptr::cleanup();
}

Expand Down
20 changes: 1 addition & 19 deletions src/librustrt/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ pub const RED_ZONE: uint = 20 * 1024;
#[cfg(not(test))] // in testing, use the original libstd's version
#[lang = "stack_exhausted"]
extern fn stack_exhausted() {
use core::prelude::*;
use alloc::boxed::Box;
use local::Local;
use task::Task;
use core::intrinsics;

unsafe {
Expand Down Expand Up @@ -104,21 +100,7 @@ extern fn stack_exhausted() {
// #9854 - unwinding on windows through __morestack has never worked
// #2361 - possible implementation of not using landing pads

let task: Option<Box<Task>> = Local::try_take();
let name = match task {
Some(ref task) => {
task.name.as_ref().map(|n| n.as_slice())
}
None => None
};
let name = name.unwrap_or("<unknown>");

// See the message below for why this is not emitted to the
// task's logger. This has the additional conundrum of the
// logger may not be initialized just yet, meaning that an FFI
// call would happen to initialized it (calling out to libuv),
// and the FFI call needs 2MB of stack when we just ran out.
rterrln!("task '{}' has overflowed its stack", name);
::stack_overflow::report();

intrinsics::abort();
}
Expand Down
Loading

0 comments on commit a10917a

Please sign in to comment.