Open
Description
Rust attempts to catch stack overflow by installing a guard page at the end of the valid stack range. This guard page is stored in TLS and is torn down when a thread exits (including the main thread).
However other thread local data may run drop
after this guard page is torn down. Stack overflows occurring in these drop
implementations are not detected by Rust. (It may be backstopped by the OS, but this is system dependent.)
To reproduce:
use std::thread_local;
struct First;
struct Second;
impl Drop for First {
fn drop(&mut self) {
// First is materialized in TLS in main().
// Second is materialized in First's dtor, thereby registering a
// dtor for Second.
// This dtor is guaranteed to run after the Thread data (including
// its guard page) has been unmapped.
SECOND.with(|_s| {} );
}
}
impl Drop for Second {
fn drop(&mut self) {
// Trigger a stack overflow.
recurse(0);
}
}
thread_local! {
static FIRST: First = First;
}
thread_local! {
static SECOND: Second = Second;
}
// Triggers a stack overflow.
fn recurse(count: usize) {
if count < usize::MAX {
recurse(count + 1);
}
println!("{} bottles of beer on the wall", count);
}
fn main() {
FIRST.with(|_f| {});
}
This causes a SIGILL
on macOS and a SIGSEGV
on Linux. In both cases I confirmed that Rust's stack overflow signal handler is not run.
Reproduced on rust stable and master branch.