Skip to content

host_bindgen! generated code panics on guest errors instead of returning Result #1316

@jsturtevant

Description

@jsturtevant

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

  1. Host calls failable.will_trap() — the macro-generated export wrapper
  2. Inside the wrapper, Callable::call() invokes the guest function
  3. The guest panics → hyperlight guest runtime aborts the VM
  4. Callable::call() returns Err(GuestAborted(...))
  5. The macro-generated code panics: let Ok(ret) = ret else { panic!(...) };
  6. 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 -a

Windows (PowerShell):

cmd /c ver

Hypervisor

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-Table

Extra Info

src/hyperlight_component_util/src/host.rs line 69:

let ::std::result::Result::Ok(#ret) = #ret else { panic!("bad return from guest {:?}", #ret) };

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions