Skip to content

Commit 9082529

Browse files
committed
Directly implement native exception raise methods in miri
Windows still needs the old custom ABI as SEH unwinding isn't supported by miri. Unlike DWARF unwinding it preserves all stack frames until right after the do_catch function has executed. Because of this panic_unwind stack allocates the exception object. Miri can't currently model unwinding without destroying stack frames and as such will report a use-after-free of the exception object.
1 parent 92110c2 commit 9082529

File tree

3 files changed

+113
-0
lines changed

3 files changed

+113
-0
lines changed

src/shims/foreign_items.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ impl DynSym {
3838
pub enum EmulateForeignItemResult {
3939
/// The caller is expected to jump to the return block.
4040
NeedsJumping,
41+
/// The caller is expected to jump to the unwind block.
42+
NeedsUnwind,
4143
/// Jumping has already been taken care of.
4244
AlreadyJumped,
4345
/// The item is not supported.
@@ -126,6 +128,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
126128
trace!("{:?}", this.dump_place(dest));
127129
this.go_to_block(ret);
128130
}
131+
EmulateForeignItemResult::NeedsUnwind => {
132+
// Jump to the unwind block to begin unwinding.
133+
this.unwind_to_block(unwind)?;
134+
}
129135
EmulateForeignItemResult::AlreadyJumped => (),
130136
EmulateForeignItemResult::NotSupported => {
131137
if let Some(body) = this.lookup_exported_symbol(link_name)? {

src/shims/unix/foreign_items.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
729729
this.write_scalar(Scalar::from_i32(-1), dest)?;
730730
}
731731

732+
"_Unwind_RaiseException" => {
733+
trace!("_Unwind_RaiseException: {:?}", this.frame().instance);
734+
735+
// Get the raw pointer stored in arg[0] (the panic payload).
736+
let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?;
737+
let payload = this.read_scalar(payload)?;
738+
let thread = this.active_thread_mut();
739+
thread.panic_payloads.push(payload);
740+
741+
return Ok(EmulateForeignItemResult::NeedsUnwind);
742+
}
743+
732744
// Platform-specific shims
733745
_ => {
734746
let target_os = &*this.tcx.sess.target.os;

tests/pass/panic/unwind_dwarf.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//@only-target-linux
2+
#![feature(core_intrinsics, panic_unwind, rustc_attrs)]
3+
#![allow(internal_features)]
4+
5+
//! Unwinding using `_Unwind_RaiseException`
6+
7+
extern crate unwind as uw;
8+
9+
use std::any::Any;
10+
use std::ptr;
11+
12+
#[repr(C)]
13+
struct Exception {
14+
_uwe: uw::_Unwind_Exception,
15+
cause: Box<dyn Any + Send>,
16+
}
17+
18+
pub fn panic(data: Box<dyn Any + Send>) -> u32 {
19+
let exception = Box::new(Exception {
20+
_uwe: uw::_Unwind_Exception {
21+
exception_class: rust_exception_class(),
22+
exception_cleanup,
23+
private: [core::ptr::null(); uw::unwinder_private_data_size],
24+
},
25+
cause: data,
26+
});
27+
let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
28+
return unsafe { uw::_Unwind_RaiseException(exception_param) as u32 };
29+
30+
extern "C" fn exception_cleanup(
31+
_unwind_code: uw::_Unwind_Reason_Code,
32+
_exception: *mut uw::_Unwind_Exception,
33+
) {
34+
std::process::abort();
35+
}
36+
}
37+
38+
pub unsafe fn rust_panic_cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
39+
let exception = ptr as *mut uw::_Unwind_Exception;
40+
if (*exception).exception_class != rust_exception_class() {
41+
std::process::abort();
42+
}
43+
44+
let exception = exception.cast::<Exception>();
45+
46+
let exception = Box::from_raw(exception as *mut Exception);
47+
exception.cause
48+
}
49+
50+
fn rust_exception_class() -> uw::_Unwind_Exception_Class {
51+
// M O Z \0 R U S T -- vendor, language
52+
0x4d4f5a_00_52555354
53+
}
54+
55+
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
56+
struct Data<F, R> {
57+
f: Option<F>,
58+
r: Option<R>,
59+
p: Option<Box<dyn Any + Send>>,
60+
}
61+
62+
let mut data = Data { f: Some(f), r: None, p: None };
63+
64+
let data_ptr = ptr::addr_of_mut!(data) as *mut u8;
65+
unsafe {
66+
return if std::intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
67+
Ok(data.r.take().unwrap())
68+
} else {
69+
Err(data.p.take().unwrap())
70+
};
71+
}
72+
73+
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
74+
unsafe {
75+
let data = &mut *data.cast::<Data<F, R>>();
76+
let f = data.f.take().unwrap();
77+
data.r = Some(f());
78+
}
79+
}
80+
81+
#[rustc_nounwind]
82+
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
83+
unsafe {
84+
let obj = rust_panic_cleanup(payload);
85+
(*data.cast::<Data<F, R>>()).p = Some(obj);
86+
}
87+
}
88+
}
89+
90+
fn main() {
91+
assert_eq!(
92+
catch_unwind(|| panic(Box::new(42))).unwrap_err().downcast::<i32>().unwrap(),
93+
Box::new(42)
94+
);
95+
}

0 commit comments

Comments
 (0)