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.
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
dropafter this guard page is torn down. Stack overflows occurring in thesedropimplementations are not detected by Rust. (It may be backstopped by the OS, but this is system dependent.)To reproduce:
This causes a
SIGILLon macOS and aSIGSEGVon Linux. In both cases I confirmed that Rust's stack overflow signal handler is not run.Reproduced on rust stable and master branch.