about summary refs log tree commit diff
path: root/library/panic_unwind/src
diff options
context:
space:
mode:
authorAmanieu d'Antras <amanieu@gmail.com>2020-03-21 07:50:38 +0000
committerAmanieu d'Antras <amanieu@gmail.com>2020-08-27 21:08:30 +0100
commit239f833ed1842ce7ce5c9989871a9ce9b1ea3546 (patch)
tree78aa987848dfbed8edd6def955ab5625acc1ef17 /library/panic_unwind/src
parent118860a7e76daaac3564c7655d46ac65a14fc612 (diff)
downloadrust-239f833ed1842ce7ce5c9989871a9ce9b1ea3546.tar.gz
rust-239f833ed1842ce7ce5c9989871a9ce9b1ea3546.zip
Abort when catch_unwind catches a foreign exception
Diffstat (limited to 'library/panic_unwind/src')
-rw-r--r--library/panic_unwind/src/dwarf/eh.rs16
-rw-r--r--library/panic_unwind/src/emcc.rs46
-rw-r--r--library/panic_unwind/src/gcc.rs26
-rw-r--r--library/panic_unwind/src/lib.rs3
-rw-r--r--library/panic_unwind/src/seh.rs12
5 files changed, 59 insertions, 44 deletions
diff --git a/library/panic_unwind/src/dwarf/eh.rs b/library/panic_unwind/src/dwarf/eh.rs
index 302478cfac8..8ce4dcd2acd 100644
--- a/library/panic_unwind/src/dwarf/eh.rs
+++ b/library/panic_unwind/src/dwarf/eh.rs
@@ -51,11 +51,7 @@ pub enum EHAction {
 
 pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
 
-pub unsafe fn find_eh_action(
-    lsda: *const u8,
-    context: &EHContext<'_>,
-    foreign_exception: bool,
-) -> Result<EHAction, ()> {
+pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result<EHAction, ()> {
     if lsda.is_null() {
         return Ok(EHAction::None);
     }
@@ -98,7 +94,7 @@ pub unsafe fn find_eh_action(
                     return Ok(EHAction::None);
                 } else {
                     let lpad = lpad_base + cs_lpad;
-                    return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
+                    return Ok(interpret_cs_action(cs_action, lpad));
                 }
             }
         }
@@ -123,21 +119,17 @@ pub unsafe fn find_eh_action(
                 // Can never have null landing pad for sjlj -- that would have
                 // been indicated by a -1 call site index.
                 let lpad = (cs_lpad + 1) as usize;
-                return Ok(interpret_cs_action(cs_action, lpad, foreign_exception));
+                return Ok(interpret_cs_action(cs_action, lpad));
             }
         }
     }
 }
 
-fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction {
+fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction {
     if cs_action == 0 {
         // If cs_action is 0 then this is a cleanup (Drop::drop). We run these
         // for both Rust panics and foreign exceptions.
         EHAction::Cleanup(lpad)
-    } else if foreign_exception {
-        // catch_unwind should not catch foreign exceptions, only Rust panics.
-        // Instead just continue unwinding.
-        EHAction::None
     } else {
         // Stop unwinding Rust panics at catch_unwind.
         EHAction::Catch(lpad)
diff --git a/library/panic_unwind/src/emcc.rs b/library/panic_unwind/src/emcc.rs
index a0bdb1481c6..e428f2fdaaa 100644
--- a/library/panic_unwind/src/emcc.rs
+++ b/library/panic_unwind/src/emcc.rs
@@ -8,8 +8,10 @@
 
 use alloc::boxed::Box;
 use core::any::Any;
+use core::intrinsics;
 use core::mem;
 use core::ptr;
+use core::sync::atomic::{AtomicBool, Ordering};
 use libc::{self, c_int};
 use unwind as uw;
 
@@ -47,6 +49,11 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
 };
 
 struct Exception {
+    // This is necessary because C++ code can capture our execption with
+    // std::exception_ptr and rethrow it multiple times, possibly even in
+    // another thread.
+    caught: AtomicBool,
+
     // This needs to be an Option because the object's lifetime follows C++
     // semantics: when catch_unwind moves the Box out of the exception it must
     // still leave the exception object in a valid state because its destructor
@@ -55,11 +62,27 @@ struct Exception {
 }
 
 pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
-    assert!(!ptr.is_null());
-    let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void) as *mut Exception;
-    let ex = (*adjusted_ptr).data.take();
+    // intrinsics::try actually gives us a pointer to this structure.
+    #[repr(C)]
+    struct CatchData {
+        ptr: *mut u8,
+        is_rust_panic: bool,
+    }
+    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 {
+        super::__rust_foreign_exception();
+    };
     __cxa_end_catch();
-    ex.unwrap()
+    out
 }
 
 pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
@@ -68,25 +91,16 @@ 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 { data: Some(data) });
+    ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) });
     __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
 }
 
-// On WASM and ARM, the destructor returns the pointer to the object.
-cfg_if::cfg_if! {
-    if #[cfg(any(target_arch = "arm", target_arch = "wasm32"))] {
-        type DestructorRet = *mut libc::c_void;
-    } else {
-        type DestructorRet = ();
-    }
-}
-extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> DestructorRet {
+extern "C" fn exception_cleanup(ptr: *mut libc::c_void) -> *mut libc::c_void {
     unsafe {
         if let Some(b) = (ptr as *mut Exception).read().data {
             drop(b);
             super::__rust_drop_panic();
         }
-        #[cfg(any(target_arch = "arm", target_arch = "wasm32"))]
         ptr
     }
 }
@@ -109,7 +123,7 @@ extern "C" {
     fn __cxa_throw(
         thrown_exception: *mut libc::c_void,
         tinfo: *const TypeInfo,
-        dest: extern "C" fn(*mut libc::c_void) -> DestructorRet,
+        dest: extern "C" fn(*mut libc::c_void) -> *mut libc::c_void,
     ) -> !;
     fn __gxx_personality_v0(
         version: c_int,
diff --git a/library/panic_unwind/src/gcc.rs b/library/panic_unwind/src/gcc.rs
index f5d83c21da0..85a2a18947d 100644
--- a/library/panic_unwind/src/gcc.rs
+++ b/library/panic_unwind/src/gcc.rs
@@ -73,8 +73,14 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
 }
 
 pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
-    let exception = Box::from_raw(ptr as *mut Exception);
-    exception.cause
+    let exception = ptr as *mut uw::_Unwind_Exception;
+    if (*exception).exception_class != rust_exception_class() {
+        uw::_Unwind_DeleteException(exception);
+        super::__rust_foreign_exception();
+    } else {
+        let exception = Box::from_raw(exception as *mut Exception);
+        exception.cause
+    }
 }
 
 // Rust's exception class identifier.  This is used by personality routines to
@@ -164,9 +170,7 @@ cfg_if::cfg_if! {
             // _Unwind_Context in our libunwind bindings and fetch the required data from there
             // directly, bypassing DWARF compatibility functions.
 
-            let exception_class = (*exception_object).exception_class;
-            let foreign_exception = exception_class != rust_exception_class();
-            let eh_action = match find_eh_action(context, foreign_exception) {
+            let eh_action = match find_eh_action(context) {
                 Ok(action) => action,
                 Err(_) => return uw::_URC_FAILURE,
             };
@@ -221,15 +225,14 @@ cfg_if::cfg_if! {
         // and indirectly on Windows x86_64 via SEH.
         unsafe extern "C" fn rust_eh_personality_impl(version: c_int,
                                                       actions: uw::_Unwind_Action,
-                                                      exception_class: uw::_Unwind_Exception_Class,
+                                                      _exception_class: uw::_Unwind_Exception_Class,
                                                       exception_object: *mut uw::_Unwind_Exception,
                                                       context: *mut uw::_Unwind_Context)
                                                       -> uw::_Unwind_Reason_Code {
             if version != 1 {
                 return uw::_URC_FATAL_PHASE1_ERROR;
             }
-            let foreign_exception = exception_class != rust_exception_class();
-            let eh_action = match find_eh_action(context, foreign_exception) {
+            let eh_action = match find_eh_action(context) {
                 Ok(action) => action,
                 Err(_) => return uw::_URC_FATAL_PHASE1_ERROR,
             };
@@ -293,10 +296,7 @@ cfg_if::cfg_if! {
     }
 }
 
-unsafe fn find_eh_action(
-    context: *mut uw::_Unwind_Context,
-    foreign_exception: bool,
-) -> Result<EHAction, ()> {
+unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> Result<EHAction, ()> {
     let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
     let mut ip_before_instr: c_int = 0;
     let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
@@ -308,7 +308,7 @@ unsafe fn find_eh_action(
         get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
         get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
     };
-    eh::find_eh_action(lsda, &eh_context, foreign_exception)
+    eh::find_eh_action(lsda, &eh_context)
 }
 
 // Frame unwind info registration
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index 7d14893c4cc..ea296ee0bc3 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -88,6 +88,9 @@ extern "C" {
     /// Handler in libstd called when a panic object is dropped outside of
     /// `catch_unwind`.
     fn __rust_drop_panic() -> !;
+
+    /// Handler in libstd called when a foreign exception is caught.
+    fn __rust_foreign_exception() -> !;
 }
 
 mod dwarf;
diff --git a/library/panic_unwind/src/seh.rs b/library/panic_unwind/src/seh.rs
index 1f812f8df61..eca169373f3 100644
--- a/library/panic_unwind/src/seh.rs
+++ b/library/panic_unwind/src/seh.rs
@@ -309,15 +309,21 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
 
     extern "system" {
         #[unwind(allowed)]
-        pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
+        fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
     }
 
     _CxxThrowException(throw_ptr, &mut THROW_INFO as *mut _ as *mut _);
 }
 
 pub unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send> {
-    let exception = &mut *(payload as *mut Exception);
-    exception.data.take().unwrap()
+    // A NULL payload here means that we got here from the catch (...) of
+    // __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()
+    }
 }
 
 // This is required by the compiler to exist (e.g., it's a lang item), but