Skip to content

Commit de607f1

Browse files
committed
Add support for nested panics to miri
1 parent ef7f0e6 commit de607f1

File tree

6 files changed

+53
-32
lines changed

6 files changed

+53
-32
lines changed

src/tools/miri/src/concurrency/thread.rs

+13-6
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/tools/miri/src/shims/panic.rs

+2-3
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()?;

src/tools/miri/tests/fail/panic/double_panic.rs

+1-2
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
}

src/tools/miri/tests/fail/panic/double_panic.stderr

+8-21
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

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+
}
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)