Skip to content

Commit

Permalink
Fix & add panic payload tests.
Browse files Browse the repository at this point in the history
 1. Fix runtime UI test that expected drop of panic payload to unwind

 2. Add panic payload tests: ensure proper drop, and add one with catch_unwind

 3. Add test asserting proper abort of unwind in panic payload
  • Loading branch information
danielhenrymantilla committed Sep 26, 2022
1 parent 6d7ee4b commit 94118a4
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 6 deletions.
30 changes: 30 additions & 0 deletions library/std/src/panic/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,33 @@ fn panic_safety_traits() {
assert::<Arc<AssertUnwindSafe<T>>>();
}
}

#[test]
fn test_try_panic_any_message_drop_glue_does_happen() {
use crate::sync::Arc;

let count = Arc::new(());
let weak = Arc::downgrade(&count);

match super::catch_unwind(|| super::panic_any(count)) {
Ok(()) => panic!("closure did not panic"),
Err(e) if e.is::<Arc<()>>() => {}
Err(_) => panic!("closure did not panic with the expected payload"),
}
assert!(weak.upgrade().is_none());
}

#[test]
fn test_try_panic_resume_unwind_drop_glue_does_happen() {
use crate::sync::Arc;

let count = Arc::new(());
let weak = Arc::downgrade(&count);

match super::catch_unwind(|| super::resume_unwind(Box::new(count))) {
Ok(()) => panic!("closure did not panic"),
Err(e) if e.is::<Arc<()>>() => {}
Err(_) => panic!("closure did not panic with the expected payload"),
}
assert!(weak.upgrade().is_none());
}
30 changes: 28 additions & 2 deletions library/std/src/thread/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,13 +218,13 @@ fn test_try_panic_any_message_owned_str() {

#[test]
fn test_try_panic_any_message_any() {
type T = Box<dyn Any + Send>;
match thread::spawn(move || {
panic_any(Box::new(413u16) as Box<dyn Any + Send>);
panic_any(Box::new(413u16) as T);
})
.join()
{
Err(e) => {
type T = Box<dyn Any + Send>;
assert!(e.is::<T>());
let any = e.downcast::<T>().unwrap();
assert!(any.is::<u16>());
Expand All @@ -244,6 +244,32 @@ fn test_try_panic_any_message_unit_struct() {
}
}

#[test]
fn test_try_panic_any_message_drop_glue_does_happen() {
let count = Arc::new(());
let weak = Arc::downgrade(&count);

match thread::spawn(|| panic_any(count)).join() {
Ok(()) => panic!("thread did not panic"),
Err(e) if e.is::<Arc<()>>() => {}
Err(_) => panic!("thread did not panic with the expected payload"),
}
assert!(weak.upgrade().is_none());
}

#[test]
fn test_try_panic_resume_unwind_drop_glue_does_happen() {
let count = Arc::new(());
let weak = Arc::downgrade(&count);

match thread::spawn(|| crate::panic::resume_unwind(Box::new(count))).join() {
Ok(()) => panic!("thread did not panic"),
Err(e) if e.is::<Arc<()>>() => {}
Err(_) => panic!("thread did not panic with the expected payload"),
}
assert!(weak.upgrade().is_none());
}

#[test]
fn test_park_timeout_unpark_before() {
for _ in 0..10 {
Expand Down
73 changes: 73 additions & 0 deletions src/test/ui/panics/drop_in_panic_payload_does_not_unwind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// run-pass
// needs-unwind
// ignore-emscripten no processes
// ignore-sgx no processes
// ignore-wasm32-bare no unwinding panic
// ignore-avr no unwinding panic
// ignore-nvptx64 no unwinding panic

use std::{env, ops::Not, panic, process};

fn main() {
match &env::args().collect::<Vec<_>>()[..] {
[just_me] => parent(just_me),
[_me, subprocess] if subprocess == "panic" => subprocess_panic(),
[_me, subprocess] if subprocess == "resume_unwind" => subprocess_resume_unwind(),
_ => unreachable!(),
}
}

fn parent(self_exe: &str) {
// call the subprocess 1: panic with a drop bomb
let status =
process::Command::new(self_exe)
.arg("panic")
.status()
.expect("running the command should have succeeded")
;
assert!(status.success().not(), "`subprocess_panic()` is expected to have aborted");

// call the subprocess 2: resume_unwind with a drop bomb
let status =
process::Command::new(self_exe)
.arg("resume_unwind")
.status()
.expect("running the command should have succeeded")
;
assert!(status.success().not(), "`subprocess_resume_unwind()` is expected to have aborted");
}

fn subprocess_panic() {
let _ = panic::catch_unwind(|| {
struct Bomb;

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

let panic_payload = panic::catch_unwind(|| panic::panic_any(Bomb)).unwrap_err();
// Calls `Bomb::drop`, which starts unwinding. But since this is a panic payload already,
// the drop glue is amended to abort on unwind. So this ought to abort the process.
drop(panic_payload);
});
}

fn subprocess_resume_unwind() {
use panic::resume_unwind;
let _ = panic::catch_unwind(|| {
struct Bomb;

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

let panic_payload = panic::catch_unwind(|| resume_unwind(Box::new(Bomb))).unwrap_err();
// Calls `Bomb::drop`, which starts unwinding. But since this is a panic payload already,
// the drop glue is amended to abort on unwind. So this ought to abort the process.
drop(panic_payload);
});
}
7 changes: 3 additions & 4 deletions src/test/ui/runtime/rt-explody-panic-payloads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ fn main() {
[..] => std::panic::panic_any(Bomb),
}.expect("running the command should have succeeded");
println!("{:#?}", output);
let stderr = std::str::from_utf8(&output.stderr);
assert!(stderr.map(|v| {
v.ends_with("fatal runtime error: drop of the panic payload panicked\n")
}).unwrap_or(false));
let stderr = std::str::from_utf8(&output.stderr).expect("UTF-8 stderr");
// the drop of the panic payload cannot unwind anymore:
assert!(stderr.contains("fatal runtime error: drop of the panic payload panicked"));
}

0 comments on commit 94118a4

Please sign in to comment.