Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print a helpful message if unwinding aborts when it reaches a nounwind function #92828

Merged
merged 3 commits into from
Jan 22, 2022

Conversation

Amanieu
Copy link
Member

@Amanieu Amanieu commented Jan 12, 2022

This is implemented by routing TerminatorKind::Abort back through the panic handler, but with a special flag in the PanicInfo which indicates that the panic handler should not attempt to unwind the stack and should instead abort immediately.

This is useful for the planned change in rust-lang/lang-team#97 which would make Drop impls nounwind by default.

Code

#![feature(c_unwind)]

fn panic() {
    panic!()
}

extern "C" fn nounwind() {
    panic();
}

fn main() {
    nounwind();
}

Before

$ ./test
thread 'main' panicked at 'explicit panic', test.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Illegal instruction (core dumped)

After

$ ./test
thread 'main' panicked at 'explicit panic', test.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'panic in a function that cannot unwind', test.rs:7:1
stack backtrace:
   0:     0x556f8f86ec9b - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hdccefe11a6ac4396
   1:     0x556f8f88ac6c - core::fmt::write::he152b28c41466ebb
   2:     0x556f8f85d6e2 - std::io::Write::write_fmt::h0c261480ab86f3d3
   3:     0x556f8f8654fa - std::panicking::default_hook::{{closure}}::h5d7346f3ff7f6c1b
   4:     0x556f8f86512b - std::panicking::default_hook::hd85803a1376cac7f
   5:     0x556f8f865a91 - std::panicking::rust_panic_with_hook::h4dc1c5a3036257ac
   6:     0x556f8f86f079 - std::panicking::begin_panic_handler::{{closure}}::hdda1d83c7a9d34d2
   7:     0x556f8f86edc4 - std::sys_common::backtrace::__rust_end_short_backtrace::h5b70ed0cce71e95f
   8:     0x556f8f865592 - rust_begin_unwind
   9:     0x556f8f85a764 - core::panicking::panic_no_unwind::h2606ab3d78c87899
  10:     0x556f8f85b910 - test::nounwind::hade6c7ee65050347
  11:     0x556f8f85b936 - test::main::hdc6e02cb36343525
  12:     0x556f8f85b7e3 - core::ops::function::FnOnce::call_once::h4d02663acfc7597f
  13:     0x556f8f85b739 - std::sys_common::backtrace::__rust_begin_short_backtrace::h071d40135adb0101
  14:     0x556f8f85c149 - std::rt::lang_start::{{closure}}::h70dbfbf38b685e93
  15:     0x556f8f85c791 - std::rt::lang_start_internal::h798f1c0268d525aa
  16:     0x556f8f85c131 - std::rt::lang_start::h476a7ee0a0bb663f
  17:     0x556f8f85b963 - main
  18:     0x7f64c0822b25 - __libc_start_main
  19:     0x556f8f85ae8e - _start
  20:                0x0 - <unknown>
thread panicked while panicking. aborting.
Aborted (core dumped)

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jan 12, 2022
@rust-highfive
Copy link
Collaborator

r? @dtolnay

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jan 12, 2022
@rust-log-analyzer

This comment has been minimized.

// `abort` does not terminate the block, so we still need to generate
// an `unreachable` terminator after it.
bx.unreachable();
self.codegen_abort_terminator(helper, bx, terminator);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update cg_clif too?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cg_clif doesn't implement TerminatorKind::Abort (it's only used in landing pads).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While it does implement it, it seems it is indeed unreachable until unwinding is implemented. Could you add a fixme so I don't forget to implement it once I implement unwinding.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

            TerminatorKind::Resume | TerminatorKind::Abort => {
                trap_unreachable(fx, "[corruption] Unwinding bb reached.");
            }

I think it's already pretty clear that both are needed for unwinding support.

@bjorn3
Copy link
Member

bjorn3 commented Jan 13, 2022

thread panicked while panicking. aborting.

Why does this happen? Would this now cause #[panic_handler]'s that use core::intrinsics::abort() to recurse infinitely?

@Amanieu
Copy link
Member Author

Amanieu commented Jan 13, 2022

thread panicked while panicking. aborting.

Why does this happen? Would this now cause #[panic_handler]'s that use core::intrinsics::abort() to recurse infinitely?

It's from here: https://github.com/rust-lang/rust/pull/92828/files#diff-88e2a536317b831c2e958b9205fde12f5edaabefba963bdd3a7503bbdedf8da9R626

Explicit #[panic_handler] in no_std crates are unaffected unless they try to unwind the stack (they need to check PanicInfo::can_unwind first).

@nbdd0121
Copy link
Contributor

A message is certainly helpful but I am not sure whether it should be form of calling the panic handler.

I also think core::intrinsics::abort() should really just abort instead of performing a nounwind panicking. E.g. Rc/Arc count overflow explicitly calls abort() to avoid a function call for code size reasons.

Perhaps we want to introduce a new Terminate terminator that models C++ std::terminate.

@Amanieu
Copy link
Member Author

Amanieu commented Jan 15, 2022

core::intrinsics::abort doesn't use TerminatorKind::Abort, it is lowered to the LLVM intrinsic directly. TerminatorKind::Abort is only used when unwinding out of a nounwind function, similar to C++'s std::terminate.

The reason I used the panic handler is so that no_std programs can handle these just like other panic.

@Amanieu
Copy link
Member Author

Amanieu commented Jan 15, 2022

@bors try @rust-timer queue

@rust-timer
Copy link
Collaborator

Awaiting bors try build completion.

@rustbot label: +S-waiting-on-perf

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Jan 15, 2022
@bors
Copy link
Contributor

bors commented Jan 15, 2022

⌛ Trying commit b4f96b2f29c9f54fbe712aeede521591707bc284 with merge 7d5aad079d9bc00cd2d025efb0338b5f1b8cff0e...

@nbdd0121
Copy link
Contributor

core::intrinsics::abort doesn't use TerminatorKind::Abort, it is lowered to the LLVM intrinsic directly. TerminatorKind::Abort is only used when unwinding out of a nounwind function, similar to C++'s std::terminate.

In that sense probably we want to change the name of the terminator to Terminate? Maybe we also want a toggle to select terminate behaviour (nounwind panic or abort) for size-concerning programs.

The reason I used the panic handler is so that no_std programs can handle these just like other panic.

Obviously not for no_std programs that have an unwinding panic handler, or a std program that uses a panic hook that triggers unwinding. Both requires nightly (c_unwind) at the moment though.

@nbdd0121
Copy link
Contributor

I am thinking whether we should actually have an enum for the unwind action, so instead of Option<BasicBlock>, we should have

enum UnwindAction {
    None,
    Terminate,
    Cleanup(BasicBlock),
    // LSDA can also encode Handler(catch) but we only use them for the r#try intrinsic so it's not needed here
}

then for cg_gcc we can encode Terminate in LSDA (would require change in personality) and for cg_llvm we can generate call to PanicNoUnwind.

@Amanieu
Copy link
Member Author

Amanieu commented Jan 15, 2022

Obviously not for no_std programs that have an unwinding panic handler, or a std program that uses a panic hook that triggers unwinding. Both requires nightly (c_unwind) at the moment though.

This is why I added a can_unwind flag to PanicInfo which indicates whether the panic handler is allowed to unwind. The panic handler should check this flag and abort if it is set (after printing the error & backtrace to stderr).

@nbdd0121
Copy link
Contributor

This is why I added a can_unwind flag to PanicInfo which indicates whether the panic handler is allowed to unwind. The panic handler should check this flag and abort if it is set (after printing the error & backtrace to stderr).

I am aware. I mentioned that to point out that decision about this change needs to happen before c_unwind stablises.

@bors
Copy link
Contributor

bors commented Jan 15, 2022

☀️ Try build successful - checks-actions
Build commit: 7d5aad079d9bc00cd2d025efb0338b5f1b8cff0e (7d5aad079d9bc00cd2d025efb0338b5f1b8cff0e)

@rust-timer
Copy link
Collaborator

Queued 7d5aad079d9bc00cd2d025efb0338b5f1b8cff0e with parent b13a5bf, future comparison URL.

@rust-timer
Copy link
Collaborator

Finished benchmarking commit (7d5aad079d9bc00cd2d025efb0338b5f1b8cff0e): comparison url.

Summary: This benchmark run did not return any relevant changes.

If you disagree with this performance assessment, please file an issue in rust-lang/rustc-perf.

Benchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR led to changes in compiler perf.

@bors rollup=never
@rustbot label: +S-waiting-on-review -S-waiting-on-perf -perf-regression

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Jan 15, 2022
Copy link
Member

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great to me. Please r=me once PanicInfo::can_unwind has a tracking issue and the discussion above is wrapped up.

@Amanieu
Copy link
Member Author

Amanieu commented Jan 17, 2022

I am thinking whether we should actually have an enum for the unwind action, so instead of Option<BasicBlock>, we should have

enum UnwindAction {
    None,
    Terminate,
    Cleanup(BasicBlock),
    // LSDA can also encode Handler(catch) but we only use them for the r#try intrinsic so it's not needed here
}

then for cg_gcc we can encode Terminate in LSDA (would require change in personality) and for cg_llvm we can generate call to PanicNoUnwind.

I think this is a good idea, but should be done in a separate PR since this is a pretty invasive change to MIR.

@rust-log-analyzer

This comment has been minimized.

@lqd
Copy link
Member

lqd commented Jan 21, 2022

@bors retry network failure

Could not resolve host: ci-mirrors.rust-lang.org

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 21, 2022
@bors
Copy link
Contributor

bors commented Jan 21, 2022

⌛ Testing commit fe9dc6e with merge 55a060472e491d322e43b65b57db636845b4d026...

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Contributor

bors commented Jan 21, 2022

💔 Test failed - checks-actions

@bors bors added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jan 21, 2022
@Amanieu
Copy link
Member Author

Amanieu commented Jan 21, 2022

@bors r=dtolnay

@bors
Copy link
Contributor

bors commented Jan 21, 2022

📌 Commit 24588e6 has been approved by dtolnay

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jan 21, 2022
@jackh726
Copy link
Member

There weren't any perf regressions and I don't think this needs anything special in regards to rollups.

@bors rollup=maybe

matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jan 22, 2022
Print a helpful message if unwinding aborts when it reaches a nounwind function

This is implemented by routing `TerminatorKind::Abort` back through the panic handler, but with a special flag in the `PanicInfo` which indicates that the panic handler should *not* attempt to unwind the stack and should instead abort immediately.

This is useful for the planned change in rust-lang/lang-team#97 which would make `Drop` impls `nounwind` by default.

### Code

```rust
#![feature(c_unwind)]

fn panic() {
    panic!()
}

extern "C" fn nounwind() {
    panic();
}

fn main() {
    nounwind();
}
```

### Before

```
$ ./test
thread 'main' panicked at 'explicit panic', test.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Illegal instruction (core dumped)
```

### After

```
$ ./test
thread 'main' panicked at 'explicit panic', test.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'panic in a function that cannot unwind', test.rs:7:1
stack backtrace:
   0:     0x556f8f86ec9b - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hdccefe11a6ac4396
   1:     0x556f8f88ac6c - core::fmt::write::he152b28c41466ebb
   2:     0x556f8f85d6e2 - std::io::Write::write_fmt::h0c261480ab86f3d3
   3:     0x556f8f8654fa - std::panicking::default_hook::{{closure}}::h5d7346f3ff7f6c1b
   4:     0x556f8f86512b - std::panicking::default_hook::hd85803a1376cac7f
   5:     0x556f8f865a91 - std::panicking::rust_panic_with_hook::h4dc1c5a3036257ac
   6:     0x556f8f86f079 - std::panicking::begin_panic_handler::{{closure}}::hdda1d83c7a9d34d2
   7:     0x556f8f86edc4 - std::sys_common::backtrace::__rust_end_short_backtrace::h5b70ed0cce71e95f
   8:     0x556f8f865592 - rust_begin_unwind
   9:     0x556f8f85a764 - core::panicking::panic_no_unwind::h2606ab3d78c87899
  10:     0x556f8f85b910 - test::nounwind::hade6c7ee65050347
  11:     0x556f8f85b936 - test::main::hdc6e02cb36343525
  12:     0x556f8f85b7e3 - core::ops::function::FnOnce::call_once::h4d02663acfc7597f
  13:     0x556f8f85b739 - std::sys_common::backtrace::__rust_begin_short_backtrace::h071d40135adb0101
  14:     0x556f8f85c149 - std::rt::lang_start::{{closure}}::h70dbfbf38b685e93
  15:     0x556f8f85c791 - std::rt::lang_start_internal::h798f1c0268d525aa
  16:     0x556f8f85c131 - std::rt::lang_start::h476a7ee0a0bb663f
  17:     0x556f8f85b963 - main
  18:     0x7f64c0822b25 - __libc_start_main
  19:     0x556f8f85ae8e - _start
  20:                0x0 - <unknown>
thread panicked while panicking. aborting.
Aborted (core dumped)
```
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jan 22, 2022
Print a helpful message if unwinding aborts when it reaches a nounwind function

This is implemented by routing `TerminatorKind::Abort` back through the panic handler, but with a special flag in the `PanicInfo` which indicates that the panic handler should *not* attempt to unwind the stack and should instead abort immediately.

This is useful for the planned change in rust-lang/lang-team#97 which would make `Drop` impls `nounwind` by default.

### Code

```rust
#![feature(c_unwind)]

fn panic() {
    panic!()
}

extern "C" fn nounwind() {
    panic();
}

fn main() {
    nounwind();
}
```

### Before

```
$ ./test
thread 'main' panicked at 'explicit panic', test.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Illegal instruction (core dumped)
```

### After

```
$ ./test
thread 'main' panicked at 'explicit panic', test.rs:4:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'panic in a function that cannot unwind', test.rs:7:1
stack backtrace:
   0:     0x556f8f86ec9b - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hdccefe11a6ac4396
   1:     0x556f8f88ac6c - core::fmt::write::he152b28c41466ebb
   2:     0x556f8f85d6e2 - std::io::Write::write_fmt::h0c261480ab86f3d3
   3:     0x556f8f8654fa - std::panicking::default_hook::{{closure}}::h5d7346f3ff7f6c1b
   4:     0x556f8f86512b - std::panicking::default_hook::hd85803a1376cac7f
   5:     0x556f8f865a91 - std::panicking::rust_panic_with_hook::h4dc1c5a3036257ac
   6:     0x556f8f86f079 - std::panicking::begin_panic_handler::{{closure}}::hdda1d83c7a9d34d2
   7:     0x556f8f86edc4 - std::sys_common::backtrace::__rust_end_short_backtrace::h5b70ed0cce71e95f
   8:     0x556f8f865592 - rust_begin_unwind
   9:     0x556f8f85a764 - core::panicking::panic_no_unwind::h2606ab3d78c87899
  10:     0x556f8f85b910 - test::nounwind::hade6c7ee65050347
  11:     0x556f8f85b936 - test::main::hdc6e02cb36343525
  12:     0x556f8f85b7e3 - core::ops::function::FnOnce::call_once::h4d02663acfc7597f
  13:     0x556f8f85b739 - std::sys_common::backtrace::__rust_begin_short_backtrace::h071d40135adb0101
  14:     0x556f8f85c149 - std::rt::lang_start::{{closure}}::h70dbfbf38b685e93
  15:     0x556f8f85c791 - std::rt::lang_start_internal::h798f1c0268d525aa
  16:     0x556f8f85c131 - std::rt::lang_start::h476a7ee0a0bb663f
  17:     0x556f8f85b963 - main
  18:     0x7f64c0822b25 - __libc_start_main
  19:     0x556f8f85ae8e - _start
  20:                0x0 - <unknown>
thread panicked while panicking. aborting.
Aborted (core dumped)
```
bors added a commit to rust-lang-ci/rust that referenced this pull request Jan 22, 2022
…askrgr

Rollup of 9 pull requests

Successful merges:

 - rust-lang#85967 (add support for the l4-bender linker on the x86_64-unknown-l4re-uclibc tier 3 target)
 - rust-lang#92828 (Print a helpful message if unwinding aborts when it reaches a nounwind function)
 - rust-lang#93012 (Update pulldown-cmark version to fix markdown list issue)
 - rust-lang#93116 (Simplify use of `map_or`)
 - rust-lang#93132 (Increase the format version of rustdoc-json-types)
 - rust-lang#93147 (Interner cleanups)
 - rust-lang#93153 (Reject unsupported naked functions)
 - rust-lang#93170 (Add missing GUI test explanations)
 - rust-lang#93172 (rustdoc: remove dashed underline under main heading)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 9d7c8ed into rust-lang:master Jan 22, 2022
@rustbot rustbot added this to the 1.60.0 milestone Jan 22, 2022
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jan 25, 2022
make Windows abort_internal Miri-compatible

rust-lang#92828 started calling `abort_internal` on double-panics, uncovering that on Windows this function does not work in Miri because of its use of inline assembly.

Cc `@Amanieu`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.