Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
assembly and extraction without flattening.

### Changed
* `Sandbox` is the primary initialized sandbox type. `MultiUseSandbox` remains
as a deprecated alias.
* `Sandbox` lives in the private `sandbox::initialized` module and is reached
through `hyperlight_host::Sandbox` or `hyperlight_host::sandbox::Sandbox`.
`sandbox::initialized_multi_use` remains as a deprecated public path.
* Support overriding the guest log level when building or restoring initialized
snapshots.
* `Snapshot::save` now writes the guest memory blob sparsely, skipping all-zero
Expand Down
24 changes: 12 additions & 12 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -263,18 +263,18 @@ test-loom:
test-isolated target=default-target features="" :
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::outb::tests::test_log_emit_guest_log --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_is_honored_from_snapshot --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_overrides_initialized_snapshot --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_setter_survives_restore --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_can_be_disabled_and_reenabled --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_can_be_enabled_after_off_init --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_is_honored_from_snapshot --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_overrides_initialized_snapshot --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_setter_survives_restore --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_can_be_disabled_and_reenabled --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_can_be_enabled_after_off_init --exact --ignored
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --test integration_test -- log_message --exact --ignored
@# CPU vendor check, gated to known CI runner hardware
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F " + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- sandbox::snapshot::file::config::tests::cpu_vendor_current_is_recognized --exact --ignored
@# Slow host-dependent MSR audit. Run once per x86_64 CI profile.
{{ if features == "" { if hyperlight-target-arch == "x86_64" { cargo-cmd + " test --profile=" + (if target == "debug" { "dev" } else { target }) + " " + target-triple-flag + " -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::msr_tests::test_no_msr_leaks_across_restore_full_window_sweep --exact --ignored --nocapture" } else { "" } } else { "" } }}
{{ if features == "" { if hyperlight-target-arch == "x86_64" { cargo-cmd + " test --profile=" + (if target == "debug" { "dev" } else { target }) + " " + target-triple-flag + " -p hyperlight-host --lib -- sandbox::initialized::tests::msr_tests::test_no_msr_leaks_across_restore_full_window_sweep --exact --ignored --nocapture" } else { "" } } else { "" } }}
@# LAPIC-enabled MSHV can expose additional MSRs. Audit it once in debug.
{{ if features == "mshv3,hw-interrupts" { if target == "debug" { if hyperlight-target-arch == "x86_64" { cargo-cmd + " test --no-default-features -F mshv3,hw-interrupts --profile=dev " + target-triple-flag + " -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::msr_tests::test_no_msr_leaks_across_restore_full_window_sweep --exact --ignored --nocapture" } else { "" } } else { "" } } else { "" } }}
{{ if features == "mshv3,hw-interrupts" { if target == "debug" { if hyperlight-target-arch == "x86_64" { cargo-cmd + " test --no-default-features -F mshv3,hw-interrupts --profile=dev " + target-triple-flag + " -p hyperlight-host --lib -- sandbox::initialized::tests::msr_tests::test_no_msr_leaks_across_restore_full_window_sweep --exact --ignored --nocapture" } else { "" } } else { "" } } else { "" } }}
@# metrics tests
{{ cargo-cmd }} test {{ if features =="" {''} else if features=="no-default-features" {"--no-default-features" } else {"--no-default-features -F function_call_metrics," + features } }} --profile={{ if target == "debug" { "dev" } else { target } }} {{ target-triple-flag }} -p hyperlight-host --lib -- metrics::tests::test_metrics_are_emitted --exact

Expand Down Expand Up @@ -551,11 +551,11 @@ coverage-run hypervisor="kvm": ensure-cargo-llvm-cov
# isolated tests (require running separately due to global state)
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::outb::tests::test_log_emit_guest_log --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_is_honored_from_snapshot --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_overrides_initialized_snapshot --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_setter_survives_restore --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_can_be_disabled_and_reenabled --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_can_be_enabled_after_off_init --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_is_honored_from_snapshot --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_overrides_initialized_snapshot --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_setter_survives_restore --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_can_be_disabled_and_reenabled --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --lib -- sandbox::initialized::tests::from_snapshot::max_guest_log_level_can_be_enabled_after_off_init --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --test integration_test -- log_message --exact --ignored
cargo +{{nightly-toolchain}} test -p hyperlight-host --no-default-features -F function_call_metrics,{{ if hypervisor == "mshv3" { "mshv3" } else { "kvm" } }} --lib -- metrics::tests::test_metrics_are_emitted --exact
Expand Down
2 changes: 1 addition & 1 deletion docs/cancellation.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The `LinuxInterruptHandle` uses a packed atomic u8 to track execution state:
- **tid (AtomicU64)**: Thread ID where the vCPU is running
- **dropped (AtomicBool)**: Set when the corresponding VM has been dropped

The packed state enables atomic reads of RUNNING_BIT, CANCEL_BIT and DEBUG_INTERRUPT_BIT simultaneously via `get_running_cancel_debug()`. Within a single `VirtualCPU::run()` call, the CANCEL_BIT remains set across vcpu exits and re-entries (such as when calling host functions), ensuring cancellation persists until the guest call completes. However, `clear_cancel()` resets the CANCEL_BIT at the beginning of each new guest function call (specifically in `MultiUseSandbox::call`, before `VirtualCPU::run()` is called), preventing cancellation requests from affecting subsequent guest function calls.
The packed state enables atomic reads of RUNNING_BIT, CANCEL_BIT and DEBUG_INTERRUPT_BIT simultaneously via `get_running_cancel_debug()`. Within a single `VirtualCPU::run()` call, the CANCEL_BIT remains set across vcpu exits and re-entries (such as when calling host functions), ensuring cancellation persists until the guest call completes. However, `clear_cancel()` resets the CANCEL_BIT at the beginning of each new guest function call (specifically in `Sandbox::call`, before `VirtualCPU::run()` is called), preventing cancellation requests from affecting subsequent guest function calls.

### Signal Mechanism

Expand Down
6 changes: 3 additions & 3 deletions docs/how-to-debug-a-hyperlight-guest.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ To selectively disable this feature for a specific sandbox, call `guest_core_dum

## Creating a dump on demand

You can also create a core dump of the current state of the guest on demand by calling the `generate_crashdump` method on the `InitializedMultiUseSandbox` instance. This can be useful for debugging issues in the guest that do not cause crashes (e.g., a guest function that does not return).
You can also create a core dump of the current state of the guest on demand by calling the `generate_crashdump` method on the `Sandbox` instance. This can be useful for debugging issues in the guest that do not cause crashes (e.g., a guest function that does not return).

This is only available when the `crashdump` feature is enabled and then only if the sandbox
is also configured to allow core dumps (which is the default behavior).
Expand All @@ -243,10 +243,10 @@ sudo gdb -p <pid_of_your_process>
(gdb) info threads
# find the thread that is running the guest function you want to debug
(gdb) thread <thread_number>
# switch to the frame where you have access to your MultiUseSandbox instance
# switch to the frame where you have access to your Sandbox instance
(gdb) backtrace
(gdb) frame <frame_number>
# get the pointer to your MultiUseSandbox instance
# get the pointer to your Sandbox instance
# Get the sandbox pointer
(gdb) print sandbox
# Call the crashdump function with the pointer
Expand Down
2 changes: 1 addition & 1 deletion docs/hyperlight-metrics-logs-and-traces.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ This custom subscriber stores the spans and events in a buffer initialized only
**NOTE**: The spans/events attributes are truncated to fit in the allocated buffer.

The guest log level can be configured when building a sandbox and overridden
for an initialized sandbox with `MultiUseSandbox::log_level`. The override
for an initialized sandbox with `Sandbox::log_level`. The override
applies to later guest calls and is reapplied after snapshot restore.

When the storage space is filled, the guest triggers a VM Exit that sends the guest pointers to the host. The host can access the guest memory, get the data and parse it to create the `spans` and `events` using the `opentelemetry` crate which allows specifying the starting and ending timestamps
Expand Down
2 changes: 1 addition & 1 deletion docs/msr.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
A snapshot represents the state of a VM at a point in time. This includes MSR
state that can affect later execution.

After `MultiUseSandbox::restore`, the destination sandbox's MSR state must match
After `Sandbox::restore`, the destination sandbox's MSR state must match
the supplied snapshot, regardless of prior execution in the sandbox.

## How snapshot and restore work
Expand Down
2 changes: 1 addition & 1 deletion docs/virtio-host-guest-communication.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ memory. Guest producer and pool bookkeeping is normal guest state, while ring
and pool bytes live in scratch. Snapshot capture needs a canonical transport
state.

`MultiUseSandbox` tracks whether queue traffic occurred after the last
`Sandbox` tracks whether queue traffic occurred after the last
canonical boundary. A cached or clean snapshot needs no VM entry. A dirty
snapshot uses this flow:

Expand Down
8 changes: 4 additions & 4 deletions fuzz/fuzz_targets/guest_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,19 @@
use std::sync::{Mutex, OnceLock};

use hyperlight_host::func::{ParameterValue, ReturnType};
use hyperlight_host::{MultiUseSandbox, SandboxBuilder};
use hyperlight_host::{Sandbox, SandboxBuilder};
use hyperlight_testing::simple_guest_for_fuzzing_as_pathbuf;
use libfuzzer_sys::fuzz_target;
static SANDBOX: OnceLock<Mutex<MultiUseSandbox>> = OnceLock::new();
static SANDBOX: OnceLock<Mutex<Sandbox>> = OnceLock::new();

// This fuzz target tests all combinations of ReturnType and Parameters for `call_guest_function_by_name`.
// For fuzzing efficiency, we create one Sandbox and reuse it for all fuzzing iterations.
fuzz_target!(
init: {
let mu_sbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf())
let sandbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf())
.build()
.unwrap();
SANDBOX.set(Mutex::new(mu_sbox)).unwrap();
SANDBOX.set(Mutex::new(sandbox)).unwrap();
},

|data: (ReturnType, Vec<ParameterValue>)| {
Expand Down
8 changes: 4 additions & 4 deletions fuzz/fuzz_targets/guest_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ compile_error!("feature `trace` must be enabled to correctly fuzz guest trace fu
use std::sync::{Mutex, OnceLock};

use hyperlight_host::func::{ParameterValue, ReturnType, ReturnValue};
use hyperlight_host::{MultiUseSandbox, SandboxBuilder};
use hyperlight_host::{Sandbox, SandboxBuilder};
use hyperlight_testing::simple_guest_for_fuzzing_as_pathbuf;
use libfuzzer_sys::arbitrary::Arbitrary;
use libfuzzer_sys::{Corpus, fuzz_target};

static SANDBOX: OnceLock<Mutex<MultiUseSandbox>> = OnceLock::new();
static SANDBOX: OnceLock<Mutex<Sandbox>> = OnceLock::new();

#[derive(Debug)]
struct FuzzInput {
Expand Down Expand Up @@ -53,11 +53,11 @@ impl<'a> Arbitrary<'a> for FuzzInput {
// Any unexpected errors from the guest should be reported.
fuzz_target!(
init: {
let mu_sbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf())
let sandbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf())
.build()
.unwrap();

SANDBOX.set(Mutex::new(mu_sbox)).unwrap();
SANDBOX.set(Mutex::new(sandbox)).unwrap();
},

|data: FuzzInput| -> Corpus {
Expand Down
8 changes: 4 additions & 4 deletions fuzz/fuzz_targets/host_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ use std::sync::{Mutex, OnceLock};
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
use hyperlight_host::func::{ParameterValue, ReturnType};
use hyperlight_host::sandbox::SandboxConfiguration;
use hyperlight_host::{GuestBinary, HyperlightError, MultiUseSandbox, UninitializedSandbox};
use hyperlight_host::{GuestBinary, HyperlightError, Sandbox, UninitializedSandbox};
use hyperlight_testing::simple_guest_for_fuzzing_as_pathbuf;
use libfuzzer_sys::fuzz_target;

static SANDBOX: OnceLock<Mutex<MultiUseSandbox>> = OnceLock::new();
static SANDBOX: OnceLock<Mutex<Sandbox>> = OnceLock::new();

// This fuzz target tests all combinations of ReturnType and Parameters for `call_guest_function_by_name`.
// For fuzzing efficiency, we create one Sandbox and reuse it for all fuzzing iterations.
Expand All @@ -28,8 +28,8 @@ fuzz_target!(
Some(cfg)
)
.unwrap();
let mu_sbox: MultiUseSandbox = u_sbox.evolve().unwrap();
SANDBOX.set(Mutex::new(mu_sbox)).unwrap();
let sandbox: Sandbox = u_sbox.evolve().unwrap();
SANDBOX.set(Mutex::new(sandbox)).unwrap();
},

|data: (String, ReturnType, Vec<ParameterValue>)| {
Expand Down
8 changes: 4 additions & 4 deletions fuzz/fuzz_targets/host_print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

use std::sync::{Mutex, OnceLock};

use hyperlight_host::{MultiUseSandbox, SandboxBuilder};
use hyperlight_host::{Sandbox, SandboxBuilder};
use hyperlight_testing::simple_guest_for_fuzzing_as_pathbuf;
use libfuzzer_sys::{Corpus, fuzz_target};

static SANDBOX: OnceLock<Mutex<MultiUseSandbox>> = OnceLock::new();
static SANDBOX: OnceLock<Mutex<Sandbox>> = OnceLock::new();

// This fuzz target is used to test the HostPrint host function. We generate
// an arbitrary ParameterValue::String, which is passed to the guest, which passes
// it without modification to the host function.
// For fuzzing efficiency, we create one Sandbox and reuse it for all fuzzing iterations.
fuzz_target!(
init: {
let mu_sbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf())
let sandbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf())
.build()
.unwrap();
SANDBOX.set(Mutex::new(mu_sbox)).unwrap();
SANDBOX.set(Mutex::new(sandbox)).unwrap();
},

|data: String| -> Corpus {
Expand Down
Loading
Loading