-
Notifications
You must be signed in to change notification settings - Fork 163
Description
What happened?
The host_bindgen! macro generates export wrapper functions that panic! when Callable::call() returns Err (e.g., guest abort, trap, timeout). This crashes the host process. The generated code should return Result instead, allowing the host to handle guest failures gracefully.
Steps to Reproduce
- Host calls
failable.will_trap()— the macro-generated export wrapper - Inside the wrapper,
Callable::call()invokes the guest function - The guest panics → hyperlight guest runtime aborts the VM
Callable::call()returnsErr(GuestAborted(...))- The macro-generated code panics:
let Ok(ret) = ret else { panic!(...) }; - The host thread's stack unwinds → the host process crashes
Add a function to the existing witguest test that deliberately traps, then call it from the host test and observe the panic.
Step 1: Add to guest.wit
interface failable {
/// This function will deliberately trap in the guest
will-trap: func() -> string;
}
world test {
// ... existing imports/exports ...
export failable;
}Step 2: Add guest implementation in witguest/src/main.rs
impl test::wit::Failable for Guest {
fn will_trap(&mut self) -> alloc::string::String {
// Deliberately crash the guest VM.
// This simulates any guest failure: out of memory, stack overflow,
// calling an unimplemented import, untrusted code doing bad things, etc.
panic!("deliberate guest crash")
}
}
// Update TestExports to include the new interface:
impl test::wit::TestExports<Host> for Guest {
// ... existing ...
type Failable = Self;
fn failable(&mut self) -> &mut Self { self }
}Step 3: Add host test in wit_test.rs
#[test]
fn test_guest_trap_should_not_panic_host() {
// This test demonstrates that a guest trap (e.g., unreachable instruction)
// causes the host to panic, crashing the entire test process.
//
// Expected: the call should return a Result::Err that the host can handle.
// Actual: the macro-generated code panics, killing the host.
let mut sb = sb();
let failable = sb.failable();
// This will panic with:
// "bad return from guest Err(GuestAborted(...))"
// instead of returning an Err that we could handle.
let result = failable.will_trap();
// ^ never reaches here
// What we WANT to be able to write:
// let result = failable.try_will_trap(); // returns Result<String, Error>
// assert!(result.is_err());
}Step 4: Run and observe
$ cargo test test_guest_trap_should_not_panic_host
thread 'test_guest_trap_should_not_panic_host' panicked at
'bad return from guest Err(GuestAborted(15, "Exception: GeneralProtectionFault ..."))'
Expected Results
The generated trait functions should return Result<T, E> instead of T when calling into the guest, so the host can handle guest failures:
let result = failable.will_trap(); // Returns Result<String, HyperlightError>
match result {
Ok(s) => println!("guest returned: {s}"),
Err(e) => println!("guest crashed: {e}"), // host stays alive
}Actual Results
TODO: What actually happened?
Versions and Environment
Hyperlight version or commit: TODO
OS Version
Run the following to find your OS version:
Linux:
cat /etc/os-release && uname -aWindows (PowerShell):
cmd /c verHypervisor
Run the following to check hypervisor access:
Linux:
ls -la /dev/kvm /dev/mshv 2>&1; getfacl /dev/kvm /dev/mshv 2>&1; id
[ -r /dev/kvm ] && [ -w /dev/kvm ] && echo "KVM: OK" || echo "KVM: FAIL"
[ -r /dev/mshv ] && [ -w /dev/mshv ] && echo "MSHV: OK" || echo "MSHV: FAIL"Windows (Admin PowerShell):
Get-WindowsOptionalFeature -Online | Where-Object {$_.FeatureName -match 'Hyper-V|HypervisorPlatform|VirtualMachinePlatform'} | Format-TableExtra Info
src/hyperlight_component_util/src/host.rs line 69:
let ::std::result::Result::Ok(#ret) = #ret else { panic!("bad return from guest {:?}", #ret) };