about summary refs log tree commit diff
path: root/library/panic_unwind/src
diff options
context:
space:
mode:
authorGary Guo <gary@garyguo.net>2022-10-05 22:40:07 +0100
committerGary Guo <gary@garyguo.net>2022-10-23 20:30:16 +0100
commit86c65d2c1ce5aba2c1c74298c3e42696cff1d41a (patch)
tree6e1713508320bde39e1df020e1e6f438bbb9b38a /library/panic_unwind/src
parente521a8d46b4437f94ba1bb85826bb7d00ba13436 (diff)
downloadrust-86c65d2c1ce5aba2c1c74298c3e42696cff1d41a.tar.gz
rust-86c65d2c1ce5aba2c1c74298c3e42696cff1d41a.zip
Implement Rust foreign exception protection for EMCC and SEH
Diffstat (limited to 'library/panic_unwind/src')
-rw-r--r--library/panic_unwind/src/emcc.rs37
-rw-r--r--library/panic_unwind/src/seh.rs20
2 files changed, 42 insertions, 15 deletions
diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs
index 7c233c7c3a1..57e817ce6ad 100644
--- a/library/panic_unwind/src/emcc.rs
+++ b/library/panic_unwind/src/emcc.rs
@@ -47,7 +47,12 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
     name: b"rust_panic\0".as_ptr(),
 };
 
+// NOTE(nbdd0121): The `canary` field will be part of stable ABI after `c_unwind` stabilization.
+#[repr(C)]
 struct Exception {
+    // See `gcc.rs` on why this is present. We already have a static here so just use it.
+    canary: *const TypeInfo,
+
     // This is necessary because C++ code can capture our exception with
     // std::exception_ptr and rethrow it multiple times, possibly even in
     // another thread.
@@ -70,16 +75,21 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
     let catch_data = &*(ptr as *mut CatchData);
 
     let adjusted_ptr = __cxa_begin_catch(catch_data.ptr as *mut libc::c_void) as *mut Exception;
-    let out = if catch_data.is_rust_panic {
-        let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst);
-        if was_caught {
-            // Since cleanup() isn't allowed to panic, we just abort instead.
-            intrinsics::abort();
-        }
-        (*adjusted_ptr).data.take().unwrap()
-    } else {
+    if !catch_data.is_rust_panic {
         super::__rust_foreign_exception();
-    };
+    }
+
+    let canary = ptr::addr_of!((*adjusted_ptr).canary).read();
+    if !ptr::eq(canary, &EXCEPTION_TYPE_INFO) {
+        super::__rust_foreign_exception();
+    }
+
+    let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst);
+    if was_caught {
+        // Since cleanup() isn't allowed to panic, we just abort instead.
+        intrinsics::abort();
+    }
+    let out = (*adjusted_ptr).data.take().unwrap();
     __cxa_end_catch();
     out
 }
@@ -90,7 +100,14 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
     if exception.is_null() {
         return uw::_URC_FATAL_PHASE1_ERROR as u32;
     }
-    ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) });
+    ptr::write(
+        exception,
+        Exception {
+            canary: &EXCEPTION_TYPE_INFO,
+            caught: AtomicBool::new(false),
+            data: Some(data),
+        },
+    );
     __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
 }
 
diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs
index 6b8d0656861..651115a8248 100644
--- a/library/panic_unwind/src/seh.rs
+++ b/library/panic_unwind/src/seh.rs
@@ -49,9 +49,15 @@
 use alloc::boxed::Box;
 use core::any::Any;
 use core::mem::{self, ManuallyDrop};
+use core::ptr;
 use libc::{c_int, c_uint, c_void};
 
+// NOTE(nbdd0121): The `canary` field will be part of stable ABI after `c_unwind` stabilization.
+#[repr(C)]
 struct Exception {
+    // See `gcc.rs` on why this is present. We already have a static here so just use it.
+    canary: *const _TypeDescriptor,
+
     // This needs to be an Option because we catch the exception by reference
     // 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
@@ -235,7 +241,7 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
 macro_rules! define_cleanup {
     ($abi:tt $abi2:tt) => {
         unsafe extern $abi fn exception_cleanup(e: *mut Exception) {
-            if let Exception { data: Some(b) } = e.read() {
+            if let Exception { data: Some(b), .. } = e.read() {
                 drop(b);
                 super::__rust_drop_panic();
             }
@@ -265,7 +271,7 @@ pub 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 { data: Some(data) });
+    let mut exception = ManuallyDrop::new(Exception { canary: &TYPE_DESCRIPTOR, data: Some(data) });
     let throw_ptr = &mut exception as *mut _ as *mut _;
 
     // This... may seems surprising, and justifiably so. On 32-bit MSVC the
@@ -321,8 +327,12 @@ pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
     // __rust_try. This happens when a non-Rust foreign exception is caught.
     if payload.is_null() {
         super::__rust_foreign_exception();
-    } else {
-        let exception = &mut *(payload as *mut Exception);
-        exception.data.take().unwrap()
     }
+    let exception = payload as *mut Exception;
+    let canary = ptr::addr_of!((*exception).canary).read();
+    if !ptr::eq(canary, &TYPE_DESCRIPTOR) {
+        // A foreign Rust exception.
+        super::__rust_foreign_exception();
+    }
+    (*exception).data.take().unwrap()
 }