Skip to content

Stack overflow not caught in Drop for TLS data #111272

@ridiculousfish

Description

@ridiculousfish

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.

Metadata

Metadata

Assignees

Labels

A-runtimeArea: std's runtime and "pre-main" init for handling backtraces, unwinds, stack overflowsA-stack-probeArea: Stack probing and guard pagesA-threadArea: `std::thread`A-thread-localsArea: Thread local storage (TLS)C-bugCategory: This is a bug.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions