Skip to content

Commit 9bfb693

Browse files
committed
Make calling throw no longer UB by using "C-unwind"
1 parent 9144d59 commit 9bfb693

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

objc2/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ malloc = ["malloc_buf"]
4040
# Uses nightly features to make AutoreleasePool zero-cost even in debug mode
4141
unstable_autoreleasesafe = []
4242

43+
# Uses the c_unwind feature to make throwing safe. Nightly only
44+
unstable_c_unwind = []
45+
4346
[dependencies]
4447
malloc_buf = { version = "1.0", optional = true }
4548
objc-sys = { path = "../objc-sys", version = "=0.2.0-alpha.0" }

objc2/src/exception.rs

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,19 @@ use crate::rc::{Id, Shared};
2525
use crate::runtime::Object;
2626

2727
extern "C" {
28+
#[cfg(not(feature = "unstable_c_unwind"))]
2829
fn rust_objc_try_catch_exception(
2930
f: extern "C" fn(*mut c_void),
3031
context: *mut c_void,
3132
error: *mut *mut ffi::objc_object,
3233
) -> c_uchar;
34+
35+
#[cfg(feature = "unstable_c_unwind")]
36+
fn rust_objc_try_catch_exception(
37+
f: extern "C-unwind" fn(*mut c_void),
38+
context: *mut c_void,
39+
error: *mut *mut objc_object,
40+
) -> c_uchar;
3341
}
3442

3543
/// Throws an Objective-C exception.
@@ -57,14 +65,38 @@ pub unsafe fn throw(exception: Option<&Id<Object, Shared>>) -> ! {
5765
}
5866

5967
unsafe fn try_no_ret<F: FnOnce()>(closure: F) -> Result<(), Option<Id<Object, Shared>>> {
60-
extern "C" fn try_objc_execute_closure<F: FnOnce()>(closure: &mut Option<F>) {
61-
// This is always passed Some, so it's safe to unwrap
62-
let closure = closure.take().unwrap();
63-
closure();
64-
}
68+
#[cfg(not(feature = "unstable_c_unwind"))]
69+
let f = {
70+
extern "C" fn try_objc_execute_closure<F>(closure: &mut Option<F>)
71+
where
72+
F: FnOnce(),
73+
{
74+
// This is always passed Some, so it's safe to unwrap
75+
let closure = closure.take().unwrap();
76+
closure();
77+
}
78+
79+
let f: extern "C" fn(&mut Option<F>) = try_objc_execute_closure;
80+
let f: extern "C" fn(*mut c_void) = unsafe { mem::transmute(f) };
81+
f
82+
};
83+
84+
#[cfg(feature = "unstable_c_unwind")]
85+
let f = {
86+
extern "C-unwind" fn try_objc_execute_closure<F>(closure: &mut Option<F>)
87+
where
88+
F: FnOnce(),
89+
{
90+
// This is always passed Some, so it's safe to unwrap
91+
let closure = closure.take().unwrap();
92+
closure();
93+
}
94+
95+
let f: extern "C-unwind" fn(&mut Option<F>) = try_objc_execute_closure;
96+
let f: extern "C-unwind" fn(*mut c_void) = unsafe { mem::transmute(f) };
97+
f
98+
};
6599

66-
let f: extern "C" fn(&mut Option<F>) = try_objc_execute_closure;
67-
let f: extern "C" fn(*mut c_void) = unsafe { mem::transmute(f) };
68100
// Wrap the closure in an Option so it can be taken
69101
let mut closure = Some(closure);
70102
let context = &mut closure as *mut _ as *mut c_void;

objc2/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@
125125
feature = "unstable_autoreleasesafe",
126126
feature(negative_impls, auto_traits)
127127
)]
128+
#![cfg_attr(feature = "unstable_c_unwind", feature(c_unwind))]
128129
#![warn(elided_lifetimes_in_paths)]
129130
#![warn(missing_docs)]
130131
#![deny(non_ascii_idents)]

0 commit comments

Comments
 (0)