Skip to content

Commit a10917a

Browse files
committed
auto merge of #16388 : Zoxc/rust/stmesg, r=alexcrichton
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.
2 parents 083578d + 70cef94 commit a10917a

15 files changed

+774
-43
lines changed

src/libgreen/simple.rs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ impl Runtime for SimpleTask {
8181
}
8282
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> { None }
8383
fn stack_bounds(&self) -> (uint, uint) { fail!() }
84+
fn stack_guard(&self) -> Option<uint> { fail!() }
8485
fn can_block(&self) -> bool { true }
8586
fn wrap(self: Box<SimpleTask>) -> Box<Any+'static> { fail!() }
8687
}

src/libgreen/stack.rs

+5
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ impl Stack {
8282
}
8383
}
8484

85+
/// Point to the last writable byte of the stack
86+
pub fn guard(&self) -> *const uint {
87+
(self.start() as uint + page_size()) as *const uint
88+
}
89+
8590
/// Point to the low end of the allocated stack
8691
pub fn start(&self) -> *const uint {
8792
self.buf.as_ref().map(|m| m.data() as *const uint)

src/libgreen/task.rs

+7
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,13 @@ impl Runtime for GreenTask {
486486
c.current_stack_segment.end() as uint)
487487
}
488488

489+
fn stack_guard(&self) -> Option<uint> {
490+
let c = self.coroutine.as_ref()
491+
.expect("GreenTask.stack_guard called without a coroutine");
492+
493+
Some(c.current_stack_segment.guard() as uint)
494+
}
495+
489496
fn can_block(&self) -> bool { false }
490497

491498
fn wrap(self: Box<GreenTask>) -> Box<Any+'static> {

src/libnative/io/c_unix.rs

-10
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,6 @@ mod signal {
252252
pub status: libc::c_int,
253253
}
254254

255-
#[cfg(any(target_os = "macos", target_os = "ios"))]
256-
#[repr(C)]
257-
pub struct sigaction {
258-
pub sa_handler: extern fn(libc::c_int),
259-
sa_tramp: *mut libc::c_void,
260-
pub sa_mask: sigset_t,
261-
pub sa_flags: libc::c_int,
262-
}
263-
264-
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
265255
#[repr(C)]
266256
pub struct sigaction {
267257
pub sa_handler: extern fn(libc::c_int),

src/libnative/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ pub fn start(argc: int, argv: *const *const u8, main: proc()) -> int {
132132
rt::init(argc, argv);
133133
let mut exit_code = None;
134134
let mut main = Some(main);
135-
let mut task = task::new((my_stack_bottom, my_stack_top));
135+
let mut task = task::new((my_stack_bottom, my_stack_top),
136+
rt::thread::main_guard_page());
136137
task.name = Some(str::Slice("<main>"));
137138
drop(task.run(|| {
138139
unsafe {

src/libnative/task.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@ use io;
2929
use std::task::{TaskBuilder, Spawner};
3030

3131
/// Creates a new Task which is ready to execute as a 1:1 task.
32-
pub fn new(stack_bounds: (uint, uint)) -> Box<Task> {
32+
pub fn new(stack_bounds: (uint, uint), stack_guard: uint) -> Box<Task> {
3333
let mut task = box Task::new();
3434
let mut ops = ops();
3535
ops.stack_bounds = stack_bounds;
36+
ops.stack_guard = stack_guard;
3637
task.put_runtime(ops);
3738
return task;
3839
}
@@ -44,6 +45,7 @@ fn ops() -> Box<Ops> {
4445
io: io::IoFactory::new(),
4546
// these *should* get overwritten
4647
stack_bounds: (0, 0),
48+
stack_guard: 0
4749
}
4850
}
4951

@@ -82,6 +84,7 @@ impl Spawner for NativeSpawner {
8284
my_stack);
8385
}
8486
let mut ops = ops;
87+
ops.stack_guard = rt::thread::current_guard_page();
8588
ops.stack_bounds = (my_stack - stack + 1024, my_stack);
8689

8790
let mut f = Some(f);
@@ -115,6 +118,8 @@ struct Ops {
115118
// native tasks necessarily know their precise bounds, hence this is
116119
// optional.
117120
stack_bounds: (uint, uint),
121+
122+
stack_guard: uint
118123
}
119124

120125
impl rt::Runtime for Ops {
@@ -138,6 +143,14 @@ impl rt::Runtime for Ops {
138143

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

146+
fn stack_guard(&self) -> Option<uint> {
147+
if self.stack_guard != 0 {
148+
Some(self.stack_guard)
149+
} else {
150+
None
151+
}
152+
}
153+
141154
fn can_block(&self) -> bool { true }
142155

143156
// This function gets a little interesting. There are a few safety and

src/librustrt/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ mod local_ptr;
5151
mod thread_local_storage;
5252
mod util;
5353
mod libunwind;
54+
mod stack_overflow;
5455

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

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

118122
// FIXME(#14344) this shouldn't be necessary
@@ -151,6 +155,7 @@ pub unsafe fn cleanup() {
151155
bookkeeping::wait_for_other_tasks();
152156
at_exit_imp::run();
153157
args::cleanup();
158+
thread::cleanup();
154159
local_ptr::cleanup();
155160
}
156161

src/librustrt/stack.rs

+1-19
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,6 @@ pub const RED_ZONE: uint = 20 * 1024;
5555
#[cfg(not(test))] // in testing, use the original libstd's version
5656
#[lang = "stack_exhausted"]
5757
extern fn stack_exhausted() {
58-
use core::prelude::*;
59-
use alloc::boxed::Box;
60-
use local::Local;
61-
use task::Task;
6258
use core::intrinsics;
6359

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

107-
let task: Option<Box<Task>> = Local::try_take();
108-
let name = match task {
109-
Some(ref task) => {
110-
task.name.as_ref().map(|n| n.as_slice())
111-
}
112-
None => None
113-
};
114-
let name = name.unwrap_or("<unknown>");
115-
116-
// See the message below for why this is not emitted to the
117-
// task's logger. This has the additional conundrum of the
118-
// logger may not be initialized just yet, meaning that an FFI
119-
// call would happen to initialized it (calling out to libuv),
120-
// and the FFI call needs 2MB of stack when we just ran out.
121-
rterrln!("task '{}' has overflowed its stack", name);
103+
::stack_overflow::report();
122104

123105
intrinsics::abort();
124106
}

0 commit comments

Comments
 (0)