diff options
| author | Gary Guo <gary@garyguo.net> | 2022-10-05 22:40:07 +0100 |
|---|---|---|
| committer | Gary Guo <gary@garyguo.net> | 2022-10-23 20:30:16 +0100 |
| commit | 86c65d2c1ce5aba2c1c74298c3e42696cff1d41a (patch) | |
| tree | 6e1713508320bde39e1df020e1e6f438bbb9b38a /library/panic_unwind/src | |
| parent | e521a8d46b4437f94ba1bb85826bb7d00ba13436 (diff) | |
| download | rust-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.rs | 37 | ||||
| -rw-r--r-- | library/panic_unwind/src/seh.rs | 20 |
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() } |
