Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6dd64d3

Browse files
Rollup merge of #102721 - nbdd0121:panic, r=Amanieu
Prevent foreign Rust exceptions from being caught Fix #102715 Use the address of a static variable (which is guaranteed to be unique per copy of std) to tell apart if a Rust exception comes from local or foreign Rust code, and abort for the latter.
2 parents 73e7c3a + bfac2da commit 6dd64d3

File tree

6 files changed

+101
-20
lines changed

6 files changed

+101
-20
lines changed

‎library/panic_unwind/src/emcc.rs‎

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo {
4747
name: b"rust_panic0円".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,27 +75,38 @@ 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
}
8696

8797
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
88-
let sz = mem::size_of_val(&data);
89-
let exception = __cxa_allocate_exception(sz) as *mut Exception;
98+
let exception = __cxa_allocate_exception(mem::size_of::<Exception>()) as *mut Exception;
9099
if exception.is_null() {
91100
return uw::_URC_FATAL_PHASE1_ERROR as u32;
92101
}
93-
ptr::write(exception, Exception { caught: AtomicBool::new(false), data: Some(data) });
102+
ptr::write(
103+
exception,
104+
Exception {
105+
canary: &EXCEPTION_TYPE_INFO,
106+
caught: AtomicBool::new(false),
107+
data: Some(data),
108+
},
109+
);
94110
__cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, exception_cleanup);
95111
}
96112

‎library/panic_unwind/src/gcc.rs‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,23 @@
3838
3939
use alloc::boxed::Box;
4040
use core::any::Any;
41+
use core::ptr;
4142

4243
use unwind as uw;
4344

45+
// In case where multiple copies of std exist in a single process,
46+
// we use address of this static variable to distinguish an exception raised by
47+
// this copy and some other copy (which needs to be treated as foreign exception).
48+
static CANARY: u8 = 0;
49+
50+
// NOTE(nbdd0121)
51+
// Once `c_unwind` feature is stabilized, there will be ABI stability requirement
52+
// on this struct. The first two field must be `_Unwind_Exception` and `canary`,
53+
// as it may be accessed by a different version of the std with a different compiler.
4454
#[repr(C)]
4555
struct Exception {
4656
_uwe: uw::_Unwind_Exception,
57+
canary: *const u8,
4758
cause: Box<dyn Any + Send>,
4859
}
4960

@@ -54,6 +65,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
5465
exception_cleanup,
5566
private: [0; uw::unwinder_private_data_size],
5667
},
68+
canary: &CANARY,
5769
cause: data,
5870
});
5971
let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
@@ -75,10 +87,22 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
7587
if (*exception).exception_class != rust_exception_class() {
7688
uw::_Unwind_DeleteException(exception);
7789
super::__rust_foreign_exception();
78-
} else {
79-
let exception = Box::from_raw(exception as *mut Exception);
80-
exception.cause
8190
}
91+
92+
let exception = exception.cast::<Exception>();
93+
// Just access the canary field, avoid accessing the entire `Exception` as
94+
// it can be a foreign Rust exception.
95+
let canary = ptr::addr_of!((*exception).canary).read();
96+
if !ptr::eq(canary, &CANARY) {
97+
// A foreign Rust exception, treat it slightly differently from other
98+
// foreign exceptions, because call into `_Unwind_DeleteException` will
99+
// call into `__rust_drop_panic` which produces a confusing
100+
// "Rust panic must be rethrown" message.
101+
super::__rust_foreign_exception();
102+
}
103+
104+
let exception = Box::from_raw(exception as *mut Exception);
105+
exception.cause
82106
}
83107

84108
// Rust's exception class identifier. This is used by personality routines to

‎library/panic_unwind/src/seh.rs‎

Lines changed: 15 additions & 5 deletions
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
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# ignore-i686-pc-windows-gnu
2+
3+
# This test doesn't work on 32-bit MinGW as cdylib has its own copy of unwinder
4+
# so cross-DLL unwinding does not work.
5+
6+
include ../tools.mk
7+
8+
all:
9+
$(RUSTC) bar.rs --crate-type=cdylib
10+
$(RUSTC) foo.rs
11+
$(call RUN,foo) 2>&1 | $(CGREP) "Rust cannot catch foreign exceptions"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#![crate_type = "cdylib"]
2+
#![feature(c_unwind)]
3+
4+
#[no_mangle]
5+
extern "C-unwind" fn panic() {
6+
panic!();
7+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(c_unwind)]
2+
3+
#[cfg_attr(not(windows), link(name = "bar"))]
4+
#[cfg_attr(windows, link(name = "bar.dll"))]
5+
extern "C-unwind" {
6+
fn panic();
7+
}
8+
9+
fn main() {
10+
let _ = std::panic::catch_unwind(|| {
11+
unsafe { panic() };
12+
});
13+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /