about summary refs log tree commit diff
path: root/library/panic_unwind
diff options
context:
space:
mode:
authorGeorge Tokmaji <tokmajigeorge@gmail.com>2025-07-08 21:19:38 +0200
committerGeorge Tokmaji <tokmajigeorge@gmail.com>2025-07-08 22:07:42 +0200
commit96cdbb9ba77b9a840a1637e4dab05bb2cc64fef0 (patch)
treeba48a6a79f79c5a7fb95ec95a32c5acd1b0ae106 /library/panic_unwind
parent040e2f8b9ff2d76fbe2146d6003e297ed4532088 (diff)
downloadrust-96cdbb9ba77b9a840a1637e4dab05bb2cc64fef0.tar.gz
rust-96cdbb9ba77b9a840a1637e4dab05bb2cc64fef0.zip
Win: Use exceptions with empty data for SEH panic exception copies
instead of a new panic

For unwinding with SEH, we currently construct a C++ exception with the
panic data. Being a regular C++ exception, it interacts with the C++
exception handling machinery and can be retrieved via
`std::current_exception`, which needs to copy the exception. We can't
support that, so we panic, which throws another exception, which the
C++ runtime tries to copy and store into the exception_ptr, which panics
again, which causes the C++ runtime to store a `bad_exception` instance.

However, this doesn't work because the panics thrown by the copy
function will be dropped without being rethrown, and causes unnecessary
log spam in stderr. Fix this by directly throwing an exception without
data, which doesn't cause log spam and can be dropped without being
rethrown.
Diffstat (limited to 'library/panic_unwind')
-rw-r--r--library/panic_unwind/src/seh.rs18
1 files changed, 14 insertions, 4 deletions
diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs
index 003ac4f0cd3..668e988abff 100644
--- a/library/panic_unwind/src/seh.rs
+++ b/library/panic_unwind/src/seh.rs
@@ -61,6 +61,7 @@ struct Exception {
     // and its destructor is executed by the C++ runtime. When we take the Box
     // out of the exception, we need to leave the exception in a valid state
     // for its destructor to run without double-dropping the Box.
+    // We also construct this as None for copies of the exception.
     data: Option<Box<dyn Any + Send>>,
 }
 
@@ -264,7 +265,11 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
 // runtime under a try/catch block and the panic that we generate here will be
 // used as the result of the exception copy. This is used by the C++ runtime to
 // support capturing exceptions with std::exception_ptr, which we can't support
-// because Box<dyn Any> isn't clonable.
+// because Box<dyn Any> isn't clonable. Thus we throw an exception without data,
+// which the C++ runtime will attempt to copy, which will once again fail, and
+// a std::bad_exception instance ends up in the std::exception_ptr instance.
+// The lack of data doesn't matter because the exception will never be rethrown
+// - it is purely used to signal to the C++ runtime that copying failed.
 macro_rules! define_cleanup {
     ($abi:tt $abi2:tt) => {
         unsafe extern $abi fn exception_cleanup(e: *mut Exception) {
@@ -278,7 +283,9 @@ macro_rules! define_cleanup {
         unsafe extern $abi2 fn exception_copy(
             _dest: *mut Exception, _src: *mut Exception
         ) -> *mut Exception {
-            panic!("Rust panics cannot be copied");
+            unsafe {
+                throw_exception(None);
+            }
         }
     }
 }
@@ -291,6 +298,10 @@ cfg_if::cfg_if! {
 }
 
 pub(crate) unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
+    unsafe { throw_exception(Some(data)) }
+}
+
+unsafe fn throw_exception(data: Option<Box<dyn Any + Send>>) -> ! {
     use core::intrinsics::{AtomicOrdering, atomic_store};
 
     // _CxxThrowException executes entirely on this stack frame, so there's no
@@ -300,8 +311,7 @@ pub(crate) unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
     // The ManuallyDrop is needed here since we don't want Exception to be
     // dropped when unwinding. Instead it will be dropped by exception_cleanup
     // which is invoked by the C++ runtime.
-    let mut exception =
-        ManuallyDrop::new(Exception { canary: (&raw const TYPE_DESCRIPTOR), data: Some(data) });
+    let mut exception = ManuallyDrop::new(Exception { canary: (&raw const TYPE_DESCRIPTOR), data });
     let throw_ptr = (&raw mut exception) as *mut _;
 
     // This... may seems surprising, and justifiably so. On 32-bit MSVC the