Change:
See for an example of the problem. Here we panic without any landing pads available upstack, because we are already the top-most(-ish) frame in the first place. This is UB.
Assigning priority as discussed in the of the Prioritization Working Group. label -I-prioritize +P-critical
Playground link with full code:
FWIW, Miri does detect this. :)

Discussion:
#![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] <del> #![feature(async_stream)] </del> #![feature(arbitrary_self_types)] #![feature(array_error_internals)] #![feature(asm)] #![feature(assert_matches)] #![feature(associated_type_bounds)] <ins> #![feature(async_stream)] </ins> #![feature(atomic_mut_ptr)] <ins> #![feature(auto_traits)] </ins> #![feature(bench_black_box)] #![feature(box_syntax)] #![feature(c_variadic)]

#![feature(concat_idents)] #![feature(const_cstr_unchecked)] #![feature(const_fn_floating_point_arithmetic)] <del> #![feature(const_fn_transmute)] </del> #![feature(const_fn_fn_ptr_basics)] <ins> #![feature(const_fn_transmute)] </ins> #![feature(const_io_structs)] #![feature(const_ip)] <ins> #![feature(const_ipv4)] </ins> #![feature(const_ipv6)] #![feature(const_raw_ptr_deref)] #![feature(const_socketaddr)] <del> #![feature(const_ipv4)] </del> #![feature(container_error_extra)] #![feature(core_intrinsics)] #![feature(custom_test_frameworks)]

#![feature(nll)] #![feature(nonnull_slice_from_raw_parts)] #![feature(once_cell)] <del> #![feature(auto_traits)] </del> #![feature(panic_info_message)] #![feature(panic_internals)] #![feature(panic_unwind)]

#![feature(unboxed_closures)] #![feature(unsafe_cell_raw_get)] #![feature(unwind_attributes)] <ins> #![feature(unwrap_infallible)] </ins> #![feature(vec_into_raw_parts)] #![feature(vec_spare_capacity)] // NB: the above list is sorted to minimize merge conflicts.

main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe), argc: isize, argv: *const *const u8, <del> ) -> isize { use crate::panic; use crate::sys_common; </del> <ins> ) -> Result<isize, !> { use crate::{mem, panic, sys, sys_common}; let rt_abort = move |e| { mem::forget(e); rtabort!("initialization or cleanup bug"); }; // Guard against the code called by this function from unwinding outside of the Rust-controlled // code, which is UB. This is a requirement imposed by a combination of how the // `#[lang="start"]` attribute is implemented as well as by the implementation of the panicking // mechanism itself. // // There are a couple of instances where unwinding can begin. First is inside of the // `rt::init`, `rt::cleanup` and similar functions controlled by libstd. In those instances a // panic is a libstd implementation bug. A quite likely one too, as there isn't any way to // prevent libstd from accidentally introducing a panic to these functions. Another is from // user code from `main` or, more nefariously, as described in e.g. issue #86030. </ins> // SAFETY: Only called once during runtime initialization. <del> unsafe { sys_common::rt::init(argc, argv) }; let exit_code = panic::catch_unwind(main); sys_common::rt::cleanup(); exit_code.unwrap_or(101) as isize </del> <ins> panic::catch_unwind(move || unsafe { sys_common::rt::init(argc, argv) }).map_err(rt_abort)?; let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize) .map_err(move |e| { mem::forget(e); rtprintpanic!("drop of the panic payload panicked"); sys::abort_internal() }); panic::catch_unwind(sys_common::rt::cleanup).map_err(rt_abort)?; ret_code </ins> } #[cfg(not(test))]

argc, argv, ) <ins> .into_ok() </ins> }

<ins> // run-pass // 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; use std::process::Command; struct Bomb; impl Drop for Bomb { fn drop(&mut self) { std::panic::panic_any(Bomb); } } fn main() { let args = env::args().collect::<Vec<_>>(); let output = match &args[..] { [me] => Command::new(&me).arg("plant the").output(), [..] => 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("drop of the panic payload panicked") }).unwrap_or(false)); } </ins>