Skip to content

Commit 0e4b033

Browse files
committed
Auto merge of #110975 - Amanieu:panic_count, r=joshtriplett
Rework handling of recursive panics This PR makes 2 changes to how recursive panics works (a panic while handling a panic). 1. The panic count is no longer used to determine whether to force an immediate abort. This allows code like the following to work without aborting the process immediately: ```rust struct Double; impl Drop for Double { fn drop(&mut self) { // 2 panics are active at once, but this is fine since it is caught. std::panic::catch_unwind(|| panic!("twice")); } } let _d = Double; panic!("once"); ``` Rustc already generates appropriate code so that any exceptions escaping out of a `Drop` called in the unwind path will immediately abort the process. 2. Any panics while the panic hook is executing will force an immediate abort. This is necessary to avoid potential deadlocks like #110771 where a panic happens while holding the backtrace lock. We don't even try to print the panic message in this case since the panic may have been caused by `Display` impls. Fixes #110771
2 parents 322e0c5 + 46eddf0 commit 0e4b033

File tree

6 files changed

+53
-32
lines changed

6 files changed

+53
-32
lines changed

src/concurrency/thread.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,15 @@ pub struct Thread<'mir, 'tcx> {
133133
/// The join status.
134134
join_status: ThreadJoinStatus,
135135

136-
/// The temporary used for storing the argument of
137-
/// the call to `miri_start_panic` (the panic payload) when unwinding.
136+
/// Stack of active panic payloads for the current thread. Used for storing
137+
/// the argument of the call to `miri_start_panic` (the panic payload) when unwinding.
138138
/// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
139-
pub(crate) panic_payload: Option<Scalar<Provenance>>,
139+
///
140+
/// In real unwinding, the payload gets passed as an argument to the landing pad,
141+
/// which then forwards it to 'Resume'. However this argument is implicit in MIR,
142+
/// so we have to store it out-of-band. When there are multiple active unwinds,
143+
/// the innermost one is always caught first, so we can store them as a stack.
144+
pub(crate) panic_payloads: Vec<Scalar<Provenance>>,
140145

141146
/// Last OS error location in memory. It is a 32-bit integer.
142147
pub(crate) last_error: Option<MPlaceTy<'tcx, Provenance>>,
@@ -206,7 +211,7 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
206211
stack: Vec::new(),
207212
top_user_relevant_frame: None,
208213
join_status: ThreadJoinStatus::Joinable,
209-
panic_payload: None,
214+
panic_payloads: Vec::new(),
210215
last_error: None,
211216
on_stack_empty,
212217
}
@@ -216,7 +221,7 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
216221
impl VisitTags for Thread<'_, '_> {
217222
fn visit_tags(&self, visit: &mut dyn FnMut(BorTag)) {
218223
let Thread {
219-
panic_payload,
224+
panic_payloads: panic_payload,
220225
last_error,
221226
stack,
222227
top_user_relevant_frame: _,
@@ -226,7 +231,9 @@ impl VisitTags for Thread<'_, '_> {
226231
on_stack_empty: _, // we assume the closure captures no GC-relevant state
227232
} = self;
228233

229-
panic_payload.visit_tags(visit);
234+
for payload in panic_payload {
235+
payload.visit_tags(visit);
236+
}
230237
last_error.visit_tags(visit);
231238
for frame in stack {
232239
frame.visit_tags(visit)

src/shims/panic.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
6363
let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?;
6464
let payload = this.read_scalar(payload)?;
6565
let thread = this.active_thread_mut();
66-
assert!(thread.panic_payload.is_none(), "the panic runtime should avoid double-panics");
67-
thread.panic_payload = Some(payload);
66+
thread.panic_payloads.push(payload);
6867

6968
// Jump to the unwind block to begin unwinding.
7069
this.unwind_to_block(unwind)?;
@@ -146,7 +145,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
146145

147146
// The Thread's `panic_payload` holds what was passed to `miri_start_panic`.
148147
// This is exactly the second argument we need to pass to `catch_fn`.
149-
let payload = this.active_thread_mut().panic_payload.take().unwrap();
148+
let payload = this.active_thread_mut().panic_payloads.pop().unwrap();
150149

151150
// Push the `catch_fn` stackframe.
152151
let f_instance = this.get_ptr_fn(catch_unwind.catch_fn)?.as_instance()?;

tests/fail/panic/double_panic.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
//@error-in-other-file: the program aborted
21
//@normalize-stderr-test: "\| +\^+" -> "| ^"
3-
//@normalize-stderr-test: "unsafe \{ libc::abort\(\) \}|crate::intrinsics::abort\(\);" -> "ABORT();"
42
//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1"
53
//@normalize-stderr-test: "\n at [^\n]+" -> "$1"
64

@@ -11,6 +9,7 @@ impl Drop for Foo {
119
}
1210
}
1311
fn main() {
12+
//~^ERROR: panic in a function that cannot unwind
1413
let _foo = Foo;
1514
panic!("first");
1615
}

tests/fail/panic/double_panic.stderr

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,17 @@ thread 'main' panicked at 'first', $DIR/double_panic.rs:LL:CC
22
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
33
thread 'main' panicked at 'second', $DIR/double_panic.rs:LL:CC
44
stack backtrace:
5-
thread panicked while panicking. aborting.
6-
error: abnormal termination: the program aborted execution
7-
--> RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC
8-
|
9-
LL | ABORT();
10-
| ^ the program aborted execution
11-
|
12-
= note: inside `std::sys::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/PLATFORM/mod.rs:LL:CC
13-
= note: inside `std::panicking::rust_panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
14-
= note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
15-
= note: inside `std::sys_common::backtrace::__rust_end_short_backtrace::<[closure@std::panicking::begin_panic_handler::{closure#0}], !>` at RUSTLIB/std/src/sys_common/backtrace.rs:LL:CC
16-
= note: inside `std::panicking::begin_panic_handler` at RUSTLIB/std/src/panicking.rs:LL:CC
17-
note: inside `<Foo as std::ops::Drop>::drop`
5+
error: abnormal termination: panic in a function that cannot unwind
186
--> $DIR/double_panic.rs:LL:CC
197
|
20-
LL | panic!("second");
21-
| ^
22-
= note: inside `std::ptr::drop_in_place::<Foo> - shim(Some(Foo))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
23-
note: inside `main`
24-
--> $DIR/double_panic.rs:LL:CC
8+
LL | / fn main() {
9+
LL | |
10+
LL | | let _foo = Foo;
11+
LL | | panic!("first");
12+
LL | | }
13+
| |_^ panic in a function that cannot unwind
2514
|
26-
LL | }
27-
| ^
28-
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
= note: inside `main` at $DIR/double_panic.rs:LL:CC
2916

3017
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
3118

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//@normalize-stderr-test: "\| +\^+" -> "| ^"
2+
//@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "$1"
3+
//@normalize-stderr-test: "\n at [^\n]+" -> "$1"
4+
5+
// Checks that nested panics work correctly.
6+
7+
use std::panic::catch_unwind;
8+
9+
fn double() {
10+
struct Double;
11+
12+
impl Drop for Double {
13+
fn drop(&mut self) {
14+
let _ = catch_unwind(|| panic!("twice"));
15+
}
16+
}
17+
18+
let _d = Double;
19+
20+
panic!("once");
21+
}
22+
23+
fn main() {
24+
assert!(catch_unwind(|| double()).is_err());
25+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
thread 'main' panicked at 'once', $DIR/nested_panic_caught.rs:LL:CC
2+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
3+
thread 'main' panicked at 'twice', $DIR/nested_panic_caught.rs:LL:CC
4+
stack backtrace:

0 commit comments

Comments
 (0)