diff --git a/library/std/src/panic/tests.rs b/library/std/src/panic/tests.rs index b37d74011cc67..057274f3d2269 100644 --- a/library/std/src/panic/tests.rs +++ b/library/std/src/panic/tests.rs @@ -54,3 +54,33 @@ fn panic_safety_traits() { assert::>>(); } } + +#[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::>() => {} + 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::>() => {} + Err(_) => panic!("closure did not panic with the expected payload"), + } + assert!(weak.upgrade().is_none()); +} diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 130e47c8d44f0..db7ce8f5dd8e7 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -218,13 +218,13 @@ fn test_try_panic_any_message_owned_str() { #[test] fn test_try_panic_any_message_any() { + type T = Box; match thread::spawn(move || { - panic_any(Box::new(413u16) as Box); + panic_any(Box::new(413u16) as T); }) .join() { Err(e) => { - type T = Box; assert!(e.is::()); let any = e.downcast::().unwrap(); assert!(any.is::()); @@ -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::>() => {} + 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::>() => {} + 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 { diff --git a/src/test/ui/panics/drop_in_panic_payload_does_not_unwind.rs b/src/test/ui/panics/drop_in_panic_payload_does_not_unwind.rs new file mode 100644 index 0000000000000..4ee0fe07de85a --- /dev/null +++ b/src/test/ui/panics/drop_in_panic_payload_does_not_unwind.rs @@ -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::>()[..] { + [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); + }); +} diff --git a/src/test/ui/runtime/rt-explody-panic-payloads.rs b/src/test/ui/runtime/rt-explody-panic-payloads.rs index e2221e5df8ea1..fe223ccf11305 100644 --- a/src/test/ui/runtime/rt-explody-panic-payloads.rs +++ b/src/test/ui/runtime/rt-explody-panic-payloads.rs @@ -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")); }