TLS dtor panics abort the process

2fe77d2
Opened by Alex Crichton at 2021-01-25 03:16:11

Today a destructor in TLS which panics will abort the process:

struct Foo;

impl Drop for Foo {
    fn drop(&mut self) { panic!() }
}

thread_local!(static FOO: Foo = Foo);

pub fn main() {
    FOO.with(|_| {});
}

When compiled and run (note that it must be run on a recent system due to #19776):

thread '<main>' panicked at 'explicit panic', foo.rs:4
fatal runtime error: Could not unwind stack, error = 5
zsh: illegal hardware instruction  ./foo

The reason behind this is that the context in which TLS destructors are running is different than the execution of the main thread itself (e.g. there is no try/catch block).

Is this (a) desirable and (b) should we change it?

  1. Triage: no change in behavior here.

    Steve Klabnik at 2015-12-18 22:26:04

  2. It would be nice if this was a clean and intentional abort, which would probably be achieved whenever we make extern "C" functions abort to avoid UB (but we can do it sooner manually).

    The fatal runtime error: Could not unwind stack, error = 5 message could lean one to believe this is some sort of bug elsewhere (i.e. in the implementation of panics).

    For example, in https://github.com/rust-lang/rust/issues/63804#issuecomment-523973998 I noticed that the message doesn't have anything to do with the way proc macros and libstd('s panic runtime) interact, which is what I initially assumed.

    Eduard-Mihai Burtescu at 2019-08-23 11:00:58

  3. An especially bad side effect is that, when this happens in a test, the test doesn’t always fail.

    struct Foo;
    
    impl Drop for Foo {
        fn drop(&mut self) {
            panic!()
        }
    }
    
    thread_local!(static FOO: Foo = Foo);
    
    #[test]
    pub fn test() {
        FOO.with(|_| {});
    }
    
    $ cargo test
        Finished test [unoptimized + debuginfo] target(s) in 0.00s
         Running target/debug/deps/panicking_test-85130fa46b54f758
    
    running 1 test
    thread 'test test ... test' panicked at 'explicit panic', src/main.rs:5:9
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    ok
    
    test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
    
    fatal runtime error: failed to initiate panic, error 5
    
    $ echo $?
    0
    

    The exit status of cargo test is either 0 or 101 nondeterministically, and seems to be more likely to be 0 in a large project, which means that the bug that caused this panic can easily remain uncaught.

    Edit: This turns out to be an independent problem, due to libtest failing to actually wait for its test threads to exit; I opened a PR as #81367.

    Anders Kaseorg at 2021-01-24 22:45:51