Skip to content

Commit 6930869

Browse files
committed
Implement Rust foreign exception protection for EMCC and SEH
1 parent aa87209 commit 6930869

File tree

2 files changed

+42
-15
lines changed

2 files changed

+42
-15
lines changed

library/panic_unwind/src/emcc.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
4747
name: b"rust_panic\0".as_ptr(),
4848
};
4949

50+
// NOTE(nbdd0121): The `canary` field will be part of stable ABI after `c_unwind` stabilization.
51+
#[repr(C)]
5052
struct Exception {
53+
// See `gcc.rs` on why this is present. We already have a static here so just use it.
54+
canary: *const TypeInfo,
55+
5156
// This is necessary because C++ code can capture our exception with
5257
// std::exception_ptr and rethrow it multiple times, possibly even in
5358
// another thread.
@@ -70,16 +75,21 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
7075
let catch_data = &*(ptr as *mut CatchData);
7176

7277
let adjusted_ptr = __cxa_begin_catch(catch_data.ptr as *mut libc::c_void) as *mut Exception;
73-
let out = if catch_data.is_rust_panic {
74-
let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst);
75-
if was_caught {
76-
// Since cleanup() isn't allowed to panic, we just abort instead.
77-
intrinsics::abort();
78-
}
79-
(*adjusted_ptr).data.take().unwrap()
80-
} else {
78+
if !catch_data.is_rust_panic {
8179
super::__rust_foreign_exception();
82-
};
80+
}
81+
82+
let canary = ptr::addr_of!((*adjusted_ptr).canary).read();
83+
if !ptr::eq(canary, &EXCEPTION_TYPE_INFO) {
84+
super::__rust_foreign_exception();
85+
}
86+
87+
let was_caught = (*adjusted_ptr).caught.swap(true, Ordering::SeqCst);
88+
if was_caught {
89+
// Since cleanup() isn't allowed to panic, we just abort instead.
90+
intrinsics::abort();
91+
}
92+
let out = (*adjusted_ptr).data.take().unwrap();
8393
__cxa_end_catch();
8494
out
8595
}
@@ -90,7 +100,14 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
90100
if exception.is_null() {
91101
return uw::_URC_FATAL_PHASE1_ERROR as u32;
92102
}
93-
ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) });
103+
ptr::write(
104+
exception,
105+
Exception {
106+
canary: &EXCEPTION_TYPE_INFO,
107+
caught: AtomicBool::new(false),
108+
data: Some(data),
109+
},
110+
);
94111
__cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
95112
}
96113

library/panic_unwind/src/seh.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,15 @@
4949
use alloc::boxed::Box;
5050
use core::any::Any;
5151
use core::mem::{self, ManuallyDrop};
52+
use core::ptr;
5253
use libc::{c_int, c_uint, c_void};
5354

55+
// NOTE(nbdd0121): The `canary` field will be part of stable ABI after `c_unwind` stabilization.
56+
#[repr(C)]
5457
struct Exception {
58+
// See `gcc.rs` on why this is present. We already have a static here so just use it.
59+
canary: *const _TypeDescriptor,
60+
5561
// This needs to be an Option because we catch the exception by reference
5662
// and its destructor is executed by the C++ runtime. When we take the Box
5763
// out of the exception, we need to leave the exception in a valid state
@@ -235,7 +241,7 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
235241
macro_rules! define_cleanup {
236242
($abi:tt $abi2:tt) => {
237243
unsafe extern $abi fn exception_cleanup(e: *mut Exception) {
238-
if let Exception { data: Some(b) } = e.read() {
244+
if let Exception { data: Some(b), .. } = e.read() {
239245
drop(b);
240246
super::__rust_drop_panic();
241247
}
@@ -265,7 +271,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
265271
// The ManuallyDrop is needed here since we don't want Exception to be
266272
// dropped when unwinding. Instead it will be dropped by exception_cleanup
267273
// which is invoked by the C++ runtime.
268-
let mut exception = ManuallyDrop::new(Exception { data: Some(data) });
274+
let mut exception = ManuallyDrop::new(Exception { canary: &TYPE_DESCRIPTOR, data: Some(data) });
269275
let throw_ptr = &mut exception as *mut _ as *mut _;
270276

271277
// 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> {
321327
// __rust_try. This happens when a non-Rust foreign exception is caught.
322328
if payload.is_null() {
323329
super::__rust_foreign_exception();
324-
} else {
325-
let exception = &mut *(payload as *mut Exception);
326-
exception.data.take().unwrap()
327330
}
331+
let exception = payload as *mut Exception;
332+
let canary = ptr::addr_of!((*exception).canary).read();
333+
if !ptr::eq(canary, &TYPE_DESCRIPTOR) {
334+
// A foreign Rust exception.
335+
super::__rust_foreign_exception();
336+
}
337+
(*exception).data.take().unwrap()
328338
}

0 commit comments

Comments
 (0)