Skip to content
This repository was archived by the owner on Mar 24, 2022. It is now read-only.

Commit d80e86c

Browse files
authored
Merge pull request #572 from bytecodealliance/acf/panic-ferry
💢 Package unknown hostcall panics into termination details
2 parents f109245 + 69a1871 commit d80e86c

File tree

5 files changed

+87
-10
lines changed

5 files changed

+87
-10
lines changed

lucet-runtime/include/lucet_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ enum lucet_terminated_reason {
5050
lucet_terminated_reason_borrow_error,
5151
lucet_terminated_reason_provided,
5252
lucet_terminated_reason_remote,
53+
lucet_terminated_reason_other_panic,
5354
};
5455

5556
enum lucet_trapcode {

lucet-runtime/lucet-runtime-internals/src/c_api.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,15 +309,21 @@ pub mod lucet_result {
309309
},
310310
TerminationDetails::Provided(p) => lucet_terminated {
311311
reason: lucet_terminated_reason::Provided,
312-
provided: p
312+
provided: *p
313313
.downcast_ref()
314-
.map(|CTerminationDetails { details }| *details)
315-
.unwrap_or(ptr::null_mut()),
314+
.map(|CTerminationDetails { details }| details)
315+
.unwrap_or(&ptr::null_mut()),
316316
},
317317
TerminationDetails::Remote => lucet_terminated {
318318
reason: lucet_terminated_reason::Remote,
319319
provided: std::ptr::null_mut(),
320320
},
321+
TerminationDetails::OtherPanic(p) => lucet_terminated {
322+
reason: lucet_terminated_reason::OtherPanic,
323+
// double box the panic payload so that the pointer passed to FFI
324+
// land is thin
325+
provided: Box::into_raw(Box::new(p)) as *mut _,
326+
},
321327
},
322328
},
323329
},
@@ -372,6 +378,7 @@ pub mod lucet_result {
372378
BorrowError,
373379
Provided,
374380
Remote,
381+
OtherPanic,
375382
}
376383

377384
#[repr(C)]

lucet-runtime/lucet-runtime-internals/src/instance.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,25 @@ pub enum TerminationDetails {
12841284
Provided(Box<dyn Any + 'static>),
12851285
/// The instance was terminated by its `KillSwitch`.
12861286
Remote,
1287+
/// A panic occurred during a hostcall other than the specialized panic used to implement
1288+
/// Lucet runtime features.
1289+
///
1290+
/// Panics are raised by the Lucet runtime in order to unwind the hostcall before jumping back
1291+
/// to the host context for any of the reasons described by the variants of this type. The panic
1292+
/// payload in that case is a already a `TerminationDetails` value.
1293+
///
1294+
/// This variant is created when any type other than `TerminationDetails` is the payload of a
1295+
/// panic arising during a hostcall, meaning it was not intentionally raised by the Lucet
1296+
/// runtime.
1297+
///
1298+
/// The panic payload contained in this variant should be rethrown using
1299+
/// [`resume_unwind`](https://doc.rust-lang.org/std/panic/fn.resume_unwind.html) once returned
1300+
/// to the host context.
1301+
///
1302+
/// Note that this variant will be removed once cross-FFI unwinding support lands in
1303+
/// [Rust](https://github.com/rust-lang/rfcs/pull/2945) and
1304+
/// [Lucet](https://github.com/bytecodealliance/lucet/pull/254).
1305+
OtherPanic(Box<dyn Any + Send + 'static>),
12871306
}
12881307

12891308
impl TerminationDetails {
@@ -1334,6 +1353,7 @@ impl std::fmt::Debug for TerminationDetails {
13341353
TerminationDetails::YieldTypeMismatch => write!(f, "YieldTypeMismatch"),
13351354
TerminationDetails::Provided(_) => write!(f, "Provided(Any)"),
13361355
TerminationDetails::Remote => write!(f, "Remote"),
1356+
TerminationDetails::OtherPanic(_) => write!(f, "OtherPanic(Any)"),
13371357
}
13381358
}
13391359
}

lucet-runtime/lucet-runtime-macros/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,11 @@ pub fn lucet_hostcall(_attr: TokenStream, item: TokenStream) -> TokenStream {
111111
match res {
112112
Ok(res) => res,
113113
Err(e) => {
114-
match e.downcast::<#termination_details>() {
115-
Ok(details) => {
116-
#vmctx_mod::Vmctx::from_raw(vmctx_raw).terminate_no_unwind(*details)
117-
},
118-
Err(e) => std::panic::resume_unwind(e),
119-
}
114+
let details = match e.downcast::<#termination_details>() {
115+
Ok(details) => *details,
116+
Err(e) => #termination_details::OtherPanic(e),
117+
};
118+
#vmctx_mod::Vmctx::from_raw(vmctx_raw).terminate_no_unwind(details)
120119
}
121120
}
122121
})

lucet-runtime/lucet-runtime-tests/src/guest_fault.rs

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ macro_rules! guest_fault_common_defs {
2828
123
2929
}
3030

31+
pub struct OtherPanicPayload;
32+
33+
#[lucet_hostcall]
34+
#[no_mangle]
35+
pub fn raise_other_panic(_vmctx: &Vmctx) {
36+
panic!(OtherPanicPayload);
37+
}
38+
3139
pub static mut RECOVERABLE_PTR: *mut libc::c_char = std::ptr::null_mut();
3240

3341
#[no_mangle]
@@ -55,6 +63,17 @@ macro_rules! guest_fault_common_defs {
5563
}
5664
}
5765

66+
extern "C" fn raise_other_panic_main(vmctx: *const lucet_vmctx) {
67+
extern "C" {
68+
// actually is defined in this file
69+
fn raise_other_panic(vmctx: *const lucet_vmctx);
70+
}
71+
unsafe {
72+
raise_other_panic(vmctx);
73+
std::hint::unreachable_unchecked();
74+
}
75+
}
76+
5877
extern "C" fn infinite_loop(_vmctx: *const lucet_vmctx) {
5978
loop {}
6079
}
@@ -156,6 +175,10 @@ macro_rules! guest_fault_common_defs {
156175
"hostcall_main",
157176
FunctionPointer::from_usize(hostcall_main as usize),
158177
))
178+
.with_export_func(MockExportBuilder::new(
179+
"raise_other_panic_main",
180+
FunctionPointer::from_usize(raise_other_panic_main as usize),
181+
))
159182
.with_export_func(MockExportBuilder::new(
160183
"infinite_loop",
161184
FunctionPointer::from_usize(infinite_loop as usize),
@@ -622,7 +645,8 @@ macro_rules! guest_fault_tests {
622645
test_nonex(|| {
623646
let module = mock_traps_module();
624647
let region =
625-
<TestRegion as RegionCreate>::create(1, &Limits::default()).expect("region can be created");
648+
<TestRegion as RegionCreate>::create(1, &Limits::default())
649+
.expect("region can be created");
626650
let mut inst = region
627651
.new_instance(module)
628652
.expect("instance can be created");
@@ -648,6 +672,32 @@ macro_rules! guest_fault_tests {
648672
});
649673
}
650674

675+
#[test]
676+
fn raise_other_panic() {
677+
test_nonex(|| {
678+
let module = mock_traps_module();
679+
let region =
680+
<TestRegion as RegionCreate>::create(1, &Limits::default())
681+
.expect("region can be created");
682+
let mut inst = region
683+
.new_instance(module)
684+
.expect("instance can be created");
685+
686+
match inst.run("raise_other_panic_main", &[]) {
687+
Err(Error::RuntimeTerminated(TerminationDetails::OtherPanic(payload))) => {
688+
assert!(payload.is::<crate::common::OtherPanicPayload>());
689+
}
690+
res => panic!("unexpected result: {:?}", res),
691+
}
692+
693+
// after a fault, can reset and run a normal function; in practice we would
694+
// want to reraise the panic most of the time, but this should still work
695+
inst.reset().expect("instance resets");
696+
697+
run_onetwothree(&mut inst);
698+
});
699+
}
700+
651701
#[test]
652702
fn fatal_continue_signal_handler() {
653703
fn signal_handler_continue(

0 commit comments

Comments
 (0)