From ed7a0ce8cfa54849864f6abf635e467fe810cc15 Mon Sep 17 00:00:00 2001 From: Seth Rollins Date: Fri, 31 Jul 2026 16:18:31 -0400 Subject: [PATCH] fix(permissions): plumb `max_guest_log_level` through `MultiUseSandbox::from_snapshot` --- Justfile | 2 + src/hyperlight_host/benches/benchmarks.rs | 7 +- .../examples/crashdump/main.rs | 3 +- .../examples/guest-debugging/main.rs | 8 +- src/hyperlight_host/src/sandbox/host_funcs.rs | 2 +- .../src/sandbox/initialized_multi_use.rs | 171 +++++++++++++++--- .../src/sandbox/snapshot/file/mod.rs | 2 +- .../src/sandbox/snapshot/file_tests.rs | 96 ++++++---- .../tests/snapshot_goldens/checks.rs | 4 +- 9 files changed, 230 insertions(+), 65 deletions(-) diff --git a/Justfile b/Justfile index f69d88c41..5bb7aea8b 100644 --- a/Justfile +++ b/Justfile @@ -241,6 +241,7 @@ 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_outb_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 --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 @@ -525,6 +526,7 @@ coverage-run hypervisor="kvm": ensure-cargo-llvm-cov # isolated tests (require running separately due to global state) cargo +nightly test -p hyperlight-host --lib -- sandbox::uninitialized::tests::test_log_trace --exact --ignored cargo +nightly test -p hyperlight-host --lib -- sandbox::outb::tests::test_log_outb_log --exact --ignored + cargo +nightly test -p hyperlight-host --lib -- sandbox::initialized_multi_use::tests::from_snapshot::max_guest_log_level_is_honored_from_snapshot --exact --ignored cargo +nightly test -p hyperlight-host --test integration_test -- log_message --exact --ignored cargo +nightly 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 diff --git a/src/hyperlight_host/benches/benchmarks.rs b/src/hyperlight_host/benches/benchmarks.rs index 18d1ab4df..52994514a 100644 --- a/src/hyperlight_host/benches/benchmarks.rs +++ b/src/hyperlight_host/benches/benchmarks.rs @@ -373,7 +373,10 @@ fn bench_sandbox_from_snapshot(b: &mut criterion::Bencher, size: SandboxSize) { // Drop is not included. b.iter_batched( || (), - |_| MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None).unwrap(), + |_| { + MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None, None) + .unwrap() + }, criterion::BatchSize::PerIteration, ); } @@ -678,6 +681,7 @@ fn snapshot_file_benchmark(c: &mut Criterion) { std::sync::Arc::new(loaded), HostFunctions::default(), None, + None, ) .unwrap(); sbox.call::("Echo", "hello\n".to_string()).unwrap(); @@ -703,6 +707,7 @@ fn snapshot_file_benchmark(c: &mut Criterion) { std::sync::Arc::new(loaded), HostFunctions::default(), None, + None, ) .unwrap(); sbox.call::("Echo", "hello\n".to_string()).unwrap(); diff --git a/src/hyperlight_host/examples/crashdump/main.rs b/src/hyperlight_host/examples/crashdump/main.rs index 26a99cd26..d0fb96d10 100644 --- a/src/hyperlight_host/examples/crashdump/main.rs +++ b/src/hyperlight_host/examples/crashdump/main.rs @@ -473,7 +473,8 @@ mod tests { let mut cfg2 = SandboxConfiguration::default(); cfg2.set_guest_core_dump(true); let mut sbox2 = - MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), Some(cfg2)).unwrap(); + MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), Some(cfg2), None) + .unwrap(); let result = sbox2.call::<()>("TriggerException", ()); assert!(result.is_err(), "TriggerException should return an error"); diff --git a/src/hyperlight_host/examples/guest-debugging/main.rs b/src/hyperlight_host/examples/guest-debugging/main.rs index 4ced2f8cf..15c07cc7a 100644 --- a/src/hyperlight_host/examples/guest-debugging/main.rs +++ b/src/hyperlight_host/examples/guest-debugging/main.rs @@ -401,8 +401,12 @@ mod tests { let mut cfg = SandboxConfiguration::default(); cfg.set_guest_debug_info(DebugInfo { port: PORT }); - let mut sbox = - MultiUseSandbox::from_snapshot(snap_thread, HostFunctions::default(), Some(cfg))?; + let mut sbox = MultiUseSandbox::from_snapshot( + snap_thread, + HostFunctions::default(), + Some(cfg), + None, + )?; sbox.call::( "PrintOutput", "Hello from a from_snapshot sandbox\n".to_string(), diff --git a/src/hyperlight_host/src/sandbox/host_funcs.rs b/src/hyperlight_host/src/sandbox/host_funcs.rs index e885c1f5e..99ede4fdf 100644 --- a/src/hyperlight_host/src/sandbox/host_funcs.rs +++ b/src/hyperlight_host/src/sandbox/host_funcs.rs @@ -98,7 +98,7 @@ impl Default for HostFunctions { /// This matches the default registry installed by /// `UninitializedSandbox::new()`, so a snapshot taken from a /// regular sandbox can be loaded with - /// `MultiUseSandbox::from_snapshot(snap, HostFunctions::default(), None)` + /// `MultiUseSandbox::from_snapshot(snap, HostFunctions::default(), None, None)` /// without registering anything else. /// /// Use [`HostFunctions::empty`] for an empty registry. diff --git a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs index f43e57d39..35f532d63 100644 --- a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs +++ b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs @@ -24,6 +24,7 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{ }; use hyperlight_common::flatbuffer_wrappers::util::estimate_flatbuffer_capacity; use tracing::{Span, instrument}; +use tracing_core::LevelFilter; use super::Callable; use super::file_mapping::prepare_file_cow; @@ -164,6 +165,17 @@ impl MultiUseSandbox { /// [`SandboxConfiguration::guest_msrs`](crate::sandbox::SandboxConfiguration::guest_msrs)), /// or the load fails with an MSR mismatch. /// + /// `max_guest_log_level` sets the maximum log level passed to the + /// guest, mirroring + /// [`UninitializedSandbox::set_max_guest_log_level`](crate::UninitializedSandbox::set_max_guest_log_level). + /// If `None`, the level is determined by the `RUST_LOG` environment + /// variable, defaulting to [`LevelFilter::ERROR`] if unset. This only + /// takes effect for snapshots that still need their guest entrypoint + /// run (`NextAction::Initialise`). For a snapshot taken from an + /// already-initialized guest the level was baked into the captured + /// memory when the guest first ran, so a value supplied here has no + /// effect and a warning is logged. + /// /// # Examples /// /// From a snapshot taken on another sandbox: @@ -182,7 +194,7 @@ impl MultiUseSandbox { /// let snapshot = sandbox.snapshot()?; /// /// // Create a new sandbox directly from the snapshot - /// let mut sandbox2 = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None)?; + /// let mut sandbox2 = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None, None)?; /// let result: i32 = sandbox2.call("GetValue", ())?; /// # Ok(()) /// # } @@ -197,7 +209,7 @@ impl MultiUseSandbox { /// # fn example() -> Result<(), Box> { /// let tag = OciTag::new("latest")?; /// let snapshot = Arc::new(Snapshot::load("./guest_snapshot", tag)?); - /// let mut sandbox = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None)?; + /// let mut sandbox = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None, None)?; /// let result: String = sandbox.call("Echo", "hello".to_string())?; /// # Ok(()) /// # } @@ -207,6 +219,7 @@ impl MultiUseSandbox { snapshot: Arc, host_funcs: crate::HostFunctions, config: Option, + max_guest_log_level: Option, ) -> Result { use rand::RngExt; @@ -282,13 +295,29 @@ impl MultiUseSandbox { #[cfg(gdb)] let dbg_mem_access_hdl = Arc::new(Mutex::new(hshm.clone())); + // `max_guest_log_level` is consumed by `initialise` when it runs + // the guest entrypoint, which only happens for a preinitialised + // (`NextAction::Initialise`) snapshot. A `Call` snapshot already + // ran its entrypoint and baked the log level into the captured + // memory, so a value supplied here would be silently dropped — + // warn instead of ignoring it. + if max_guest_log_level.is_some() + && matches!(snapshot.next_action(), super::snapshot::NextAction::Call(_)) + { + tracing::warn!( + "max_guest_log_level was supplied to from_snapshot, but the snapshot is \ + an already-initialized (Call) snapshot; the log level is baked into the \ + snapshot's memory and the supplied value has no effect" + ); + } + // noop for NextAction::Call vm.initialise( peb_addr, seed, &mut hshm, &host_funcs, - None, + max_guest_log_level, #[cfg(gdb)] dbg_mem_access_hdl, ) @@ -2887,6 +2916,7 @@ mod tests { first_snapshot.clone(), HostFunctions::default(), Some(config), + None, ) .unwrap(); assert_eq!(clone.call::("ReadMSR", KERNEL_GS_BASE).unwrap(), first); @@ -2906,6 +2936,7 @@ mod tests { third_snapshot, HostFunctions::default(), Some(config), + None, ) .unwrap(); assert_eq!( @@ -2976,6 +3007,7 @@ mod tests { snapshot, HostFunctions::default(), Some(target_config), + None, ) .unwrap(); assert_eq!( @@ -3015,6 +3047,7 @@ mod tests { snapshot.clone(), HostFunctions::default(), Some(dest_config), + None, ) .unwrap(); assert_eq!(clone.call::("ReadMSR", SYSENTER_CS).unwrap(), sentinel); @@ -3074,6 +3107,7 @@ mod tests { snapshot.clone(), HostFunctions::default(), Some(config), + None, ) .expect_err("from_snapshot must reject an unrestorable snapshot MSR"); assert_snapshot_msr_index_invalid(&err); @@ -3114,6 +3148,7 @@ mod tests { snapshot.clone(), HostFunctions::default(), Some(config), + None, ) .unwrap(); let baseline: u64 = sandbox.call("ReadMSR", KERNEL_GS_BASE).unwrap(); @@ -4064,7 +4099,8 @@ mod tests { sbox.call::("AddToStatic", 11i32).unwrap(); let snapshot = sbox.snapshot().unwrap(); let mut sbox2 = - MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None, None) + .unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 11); let echoed: String = sbox2.call("Echo", "hi".to_string()).unwrap(); assert_eq!(echoed, "hi"); @@ -4076,12 +4112,89 @@ mod tests { let snap = Snapshot::from_env(GuestBinary::FilePath(path), SandboxConfiguration::default()) .unwrap(); - let mut sbox = - MultiUseSandbox::from_snapshot(Arc::new(snap), HostFunctions::default(), None) - .unwrap(); + let mut sbox = MultiUseSandbox::from_snapshot( + Arc::new(snap), + HostFunctions::default(), + None, + None, + ) + .unwrap(); assert_eq!(sbox.call::("GetStatic", ()).unwrap(), 0); } + /// `max_guest_log_level` passed to `from_snapshot` is honored for a + /// pre-init (`NextAction::Initialise`) snapshot: the guest runs its + /// entrypoint with the supplied filter, so a higher level lets more + /// guest log messages through than a lower one. Before this was + /// plumbed, `from_snapshot` always passed `None` (falling back to + /// `RUST_LOG`), so both counts below would be equal. + /// + /// Ignored because it installs a process-global `log` logger; run + /// in isolation via the `test-isolated` Justfile recipe. + #[test] + #[ignore] + fn max_guest_log_level_is_honored_from_snapshot() { + use hyperlight_common::log_level::GuestLogFilter; + use hyperlight_testing::logger::{LOGGER, Logger}; + use tracing_core::LevelFilter; + + Logger::initialize_test_logger(); + LOGGER.set_max_level(log::LevelFilter::Trace); + + // Build a fresh pre-init sandbox with the given guest log level, + // emit one guest log message at each level, and count how many + // reached the host (guest logs carry target "hyperlight_guest"). + let count_guest_logs = |max_level: LevelFilter| -> usize { + let snap = Snapshot::from_env( + GuestBinary::FilePath(simple_guest_as_string().unwrap()), + SandboxConfiguration::default(), + ) + .unwrap(); + let mut sbox = MultiUseSandbox::from_snapshot( + Arc::new(snap), + HostFunctions::default(), + None, + Some(max_level), + ) + .unwrap(); + + // Drop any log records emitted while the guest initialised. + LOGGER.clear_log_calls(); + + for level in [ + LevelFilter::TRACE, + LevelFilter::DEBUG, + LevelFilter::INFO, + LevelFilter::WARN, + LevelFilter::ERROR, + ] { + let encoded: u64 = GuestLogFilter::from(level).into(); + sbox.call::<()>("LogMessage", ("hello".to_string(), encoded as i32)) + .unwrap(); + } + + let count = (0..LOGGER.num_log_calls()) + .filter_map(|i| LOGGER.get_log_call(i)) + .filter(|c| c.target == "hyperlight_guest") + .count(); + LOGGER.clear_log_calls(); + count + }; + + let trace_count = count_guest_logs(LevelFilter::TRACE); + let error_count = count_guest_logs(LevelFilter::ERROR); + + assert!( + error_count >= 1, + "an ERROR-level guest log must reach the host, got {error_count}" + ); + assert!( + trace_count > error_count, + "TRACE must let more guest logs through than ERROR (trace={trace_count}, \ + error={error_count}); equal counts mean max_guest_log_level was ignored" + ); + } + /// Two sandboxes built from clones of one `Arc` can /// each `restore` back to it, and stay memory-isolated from /// each other in between. @@ -4091,12 +4204,20 @@ mod tests { sbox.call::("AddToStatic", 3i32).unwrap(); let snapshot = sbox.snapshot().unwrap(); - let mut a = - MultiUseSandbox::from_snapshot(snapshot.clone(), HostFunctions::default(), None) - .unwrap(); - let mut b = - MultiUseSandbox::from_snapshot(snapshot.clone(), HostFunctions::default(), None) - .unwrap(); + let mut a = MultiUseSandbox::from_snapshot( + snapshot.clone(), + HostFunctions::default(), + None, + None, + ) + .unwrap(); + let mut b = MultiUseSandbox::from_snapshot( + snapshot.clone(), + HostFunctions::default(), + None, + None, + ) + .unwrap(); assert_eq!(a.call::("GetStatic", ()).unwrap(), 3); assert_eq!(b.call::("GetStatic", ()).unwrap(), 3); @@ -4116,7 +4237,8 @@ mod tests { sbox.call::("AddToStatic", 5i32).unwrap(); let snap = sbox.snapshot().unwrap(); let mut sbox2 = - MultiUseSandbox::from_snapshot(snap, host_funcs_with_matching_add(), None).unwrap(); + MultiUseSandbox::from_snapshot(snap, host_funcs_with_matching_add(), None, None) + .unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 5); } @@ -4124,7 +4246,7 @@ mod tests { fn rejects_missing_host_function() { let mut sbox = make_sandbox_with_add(); let snap = sbox.snapshot().unwrap(); - let err = MultiUseSandbox::from_snapshot(snap, HostFunctions::default(), None) + let err = MultiUseSandbox::from_snapshot(snap, HostFunctions::default(), None, None) .expect_err("missing `Add` must be rejected"); assert!( matches!( @@ -4213,7 +4335,7 @@ mod tests { let mut hf = HostFunctions::default(); hf.register_host_function("Add", |a: String, b: String| Ok(format!("{a}{b}"))) .unwrap(); - let err = MultiUseSandbox::from_snapshot(snap, hf, None) + let err = MultiUseSandbox::from_snapshot(snap, hf, None, None) .expect_err("signature mismatch on `Add` must be rejected"); assert!( matches!( @@ -4236,7 +4358,7 @@ mod tests { let mut hf = host_funcs_with_matching_add(); hf.register_host_function("Mul", |a: i32, b: i32| Ok(a * b)) .unwrap(); - let mut sbox2 = MultiUseSandbox::from_snapshot(snap, hf, None).unwrap(); + let mut sbox2 = MultiUseSandbox::from_snapshot(snap, hf, None, None).unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 9); } @@ -4249,7 +4371,8 @@ mod tests { let snap1 = sbox.snapshot().unwrap(); let mut sbox2 = - MultiUseSandbox::from_snapshot(snap1, HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(snap1, HostFunctions::default(), None, None) + .unwrap(); sbox2.call::("AddToStatic", 6i32).unwrap(); let snap2 = sbox2.snapshot().unwrap(); @@ -4260,7 +4383,8 @@ mod tests { assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 10); let mut sbox3 = - MultiUseSandbox::from_snapshot(snap2, HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(snap2, HostFunctions::default(), None, None) + .unwrap(); assert_eq!(sbox3.call::("GetStatic", ()).unwrap(), 10); } @@ -4276,7 +4400,7 @@ mod tests { let mut hf = HostFunctions::default(); hf.register_host_function("Echo42", || Ok(42i64)).unwrap(); - let mut sbox2 = MultiUseSandbox::from_snapshot(snap, hf, None).unwrap(); + let mut sbox2 = MultiUseSandbox::from_snapshot(snap, hf, None, None).unwrap(); let got: i64 = sbox2 .call( @@ -4298,7 +4422,7 @@ mod tests { let mut hf = HostFunctions::default(); hf.register_host_function("Unrelated", |a: i32| Ok(a + 1)) .unwrap(); - let mut sbox = MultiUseSandbox::from_snapshot(Arc::new(snap), hf, None).unwrap(); + let mut sbox = MultiUseSandbox::from_snapshot(Arc::new(snap), hf, None, None).unwrap(); assert_eq!(sbox.call::("GetStatic", ()).unwrap(), 0); } @@ -4317,7 +4441,8 @@ mod tests { assert_eq!(gen2, gen1 + 1); let mut sbox2 = - MultiUseSandbox::from_snapshot(snap2, HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(snap2, HostFunctions::default(), None, None) + .unwrap(); sbox2.call::("AddToStatic", 1i32).unwrap(); let snap3 = sbox2.snapshot().unwrap(); assert_eq!(snap3.snapshot_generation(), gen2 + 1); @@ -4339,7 +4464,7 @@ mod tests { // host function, so building a sandbox from it without // `Echo42` must fail. let snap = sbox.snapshot().unwrap(); - let err = MultiUseSandbox::from_snapshot(snap, HostFunctions::default(), None) + let err = MultiUseSandbox::from_snapshot(snap, HostFunctions::default(), None, None) .expect_err("late-registered `Echo42` must be required by the new snapshot"); let msg = format!("{}", err); assert!(msg.contains("Echo42"), "got: {}", msg); diff --git a/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs b/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs index 2a0f00d2f..bdb9ba624 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs @@ -694,7 +694,7 @@ impl Snapshot { /// # fn example() -> Result<(), Box> { /// let tag = OciTag::new("latest")?; /// let snapshot = Arc::new(Snapshot::load("./guest_snapshot", tag)?); - /// let mut sandbox = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None)?; + /// let mut sandbox = MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None, None)?; /// let result: String = sandbox.call("Echo", "hello".to_string())?; /// # Ok(()) /// # } diff --git a/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs b/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs index 058895ffd..12f603942 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs @@ -97,7 +97,7 @@ fn find_snapshot_blob(oci_dir: &std::path::Path) -> std::path::PathBuf { fn from_snapshot_already_initialized_in_memory() { let snapshot = create_snapshot(); let mut sbox2 = - MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), None, None).unwrap(); let result: i32 = sbox2.call("GetStatic", ()).unwrap(); assert_eq!(result, 0); } @@ -110,7 +110,8 @@ fn from_snapshot_in_memory_pre_init() { ) .unwrap(); let mut sbox = - MultiUseSandbox::from_snapshot(Arc::new(snap), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(Arc::new(snap), HostFunctions::default(), None, None) + .unwrap(); let result: i32 = sbox.call("GetStatic", ()).unwrap(); assert_eq!(result, 0); } @@ -129,7 +130,8 @@ fn round_trip_save_load_call() { let loaded = Snapshot::checked_load(&oci, OciTag::new("latest").unwrap()).unwrap(); let mut sbox2 = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .unwrap(); let result: String = sbox2.call("Echo", "hello\n".to_string()).unwrap(); assert_eq!(result, "hello\n"); @@ -164,7 +166,8 @@ fn save_self_heals_same_length_wrong_content_snapshot_blob() { // descriptor digest. let loaded = Snapshot::checked_load(&oci, OciTag::new("latest").unwrap()).unwrap(); let mut sbox2 = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .unwrap(); let result: String = sbox2.call("Echo", "hello\n".to_string()).unwrap(); assert_eq!(result, "hello\n"); } @@ -244,7 +247,8 @@ fn snapshot_without_msrs_key_loads_and_runs() { assert_eq!(loaded.msrs(), Some(&Vec::new())); let mut sbox = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .unwrap(); assert_eq!(sbox.call::("GetStatic", ()).unwrap(), 0); } @@ -280,7 +284,7 @@ fn disk_snapshot_restores_declared_msr_value() { let mut cfg = crate::sandbox::SandboxConfiguration::default(); cfg.guest_msrs(&[SYSENTER_CS]).unwrap(); let mut sbox = - MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), Some(cfg)) + MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), Some(cfg), None) .unwrap(); assert_eq!(sbox.call::("ReadMSR", SYSENTER_CS).unwrap(), sentinel); @@ -321,7 +325,8 @@ fn disk_snapshot_restores_into_superset_guest_msrs() { let mut dest_cfg = crate::sandbox::SandboxConfiguration::default(); dest_cfg.guest_msrs(&[SYSENTER_CS, SYSENTER_ESP]).unwrap(); let mut sbox = - MultiUseSandbox::from_snapshot(loaded, HostFunctions::default(), Some(dest_cfg)).unwrap(); + MultiUseSandbox::from_snapshot(loaded, HostFunctions::default(), Some(dest_cfg), None) + .unwrap(); assert_eq!(sbox.call::("ReadMSR", SYSENTER_CS).unwrap(), sentinel); } @@ -354,7 +359,7 @@ fn disk_snapshot_non_superset_guest_msrs_rejected() { snap.save(&path, &OciTag::new("latest").unwrap()).unwrap(); let loaded = Arc::new(Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap()); - let err = MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None) + let err = MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None, None) .expect_err("loading a snapshot MSR the destination cannot restore must fail"); assert!( format!("{err:?}").contains("InvalidSnapshotMsrIndex"), @@ -409,7 +414,8 @@ fn restore_from_loaded_snapshot() { let loaded = Arc::new(Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap()); let mut sbox2 = - MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None, None) + .unwrap(); sbox2.call::("AddToStatic", 5i32).unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 5); @@ -433,7 +439,8 @@ fn restore_across_independent_oci_loads_succeeds() { let loaded1 = Arc::new(Snapshot::checked_load(&p1, OciTag::new("latest").unwrap()).unwrap()); let loaded2 = Arc::new(Snapshot::checked_load(&p2, OciTag::new("latest").unwrap()).unwrap()); - let mut sbox = MultiUseSandbox::from_snapshot(loaded2, HostFunctions::default(), None).unwrap(); + let mut sbox = + MultiUseSandbox::from_snapshot(loaded2, HostFunctions::default(), None, None).unwrap(); sbox.restore(loaded1).unwrap(); assert_eq!(sbox.call::("GetStatic", ()).unwrap(), 0); } @@ -462,7 +469,7 @@ fn cow_does_not_mutate_backing_file() { { let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); let mut sbox = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None) + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) .unwrap(); sbox.call::("AddToStatic", 99).unwrap(); } @@ -752,9 +759,13 @@ fn from_snapshot_accepts_matching_host_functions() { snap.save(&path, &OciTag::new("latest").unwrap()).unwrap(); let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); - let mut sbox2 = - MultiUseSandbox::from_snapshot(Arc::new(loaded), host_funcs_with_matching_add(), None) - .unwrap(); + let mut sbox2 = MultiUseSandbox::from_snapshot( + Arc::new(loaded), + host_funcs_with_matching_add(), + None, + None, + ) + .unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 0); } @@ -769,8 +780,9 @@ fn from_snapshot_rejects_missing_host_function() { snap.save(&path, &OciTag::new("latest").unwrap()).unwrap(); let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); - let err = MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None) - .expect_err("from_snapshot must reject a HostFunctions set missing `Add`"); + let err = + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .expect_err("from_snapshot must reject a HostFunctions set missing `Add`"); let msg = format!("{}", err); assert!( msg.contains("missing") && msg.contains("Add"), @@ -794,7 +806,7 @@ fn from_snapshot_rejects_signature_mismatch() { .unwrap(); let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); - let err = MultiUseSandbox::from_snapshot(Arc::new(loaded), hf, None) + let err = MultiUseSandbox::from_snapshot(Arc::new(loaded), hf, None, None) .expect_err("from_snapshot must reject a signature mismatch on Add"); let msg = format!("{}", err); assert!( @@ -819,7 +831,7 @@ fn from_snapshot_accepts_extra_host_functions() { .unwrap(); let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); - let mut sbox2 = MultiUseSandbox::from_snapshot(Arc::new(loaded), hf, None).unwrap(); + let mut sbox2 = MultiUseSandbox::from_snapshot(Arc::new(loaded), hf, None, None).unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 0); } @@ -839,7 +851,7 @@ fn from_snapshot_accepts_zero_arg_host_function() { hf.register_host_function("Zero", || Ok(7i64)).unwrap(); let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); - let _sbox2 = MultiUseSandbox::from_snapshot(Arc::new(loaded), hf, None) + let _sbox2 = MultiUseSandbox::from_snapshot(Arc::new(loaded), hf, None, None) .expect("zero-arg host function must round-trip through OCI"); } @@ -1905,7 +1917,8 @@ fn manifest_and_index_annotations_tolerated() { let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); let mut sbox2 = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 0); } @@ -2085,7 +2098,8 @@ fn load_round_trips() { let loaded = Snapshot::load(&path, OciTag::new("latest").unwrap()).unwrap(); let mut sbox2 = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .unwrap(); let result: String = sbox2.call("Echo", "hi\n".to_string()).unwrap(); assert_eq!(result, "hi\n"); } @@ -2723,7 +2737,8 @@ fn save_replaces_symlink_snapshot_blob_with_regular_file() { let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); let mut sbox = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .unwrap(); let result: String = sbox.call("Echo", "hello\n".to_string()).unwrap(); assert_eq!(result, "hello\n"); } @@ -2823,8 +2838,13 @@ fn round_trip_preserves_host_function_signatures() { ); // Loading and using the snapshot must accept the same signature. let loaded = Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap(); - let _ = MultiUseSandbox::from_snapshot(Arc::new(loaded), host_funcs_with_matching_add(), None) - .unwrap(); + let _ = MultiUseSandbox::from_snapshot( + Arc::new(loaded), + host_funcs_with_matching_add(), + None, + None, + ) + .unwrap(); } #[test] @@ -2844,8 +2864,8 @@ fn snapshot_with_no_host_functions_round_trips() { assert!(cfg["host_functions"].as_array().unwrap().is_empty()); let loaded = Snapshot::load(&path, OciTag::new("latest").unwrap()).unwrap(); - let _ = - MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None).unwrap(); + let _ = MultiUseSandbox::from_snapshot(Arc::new(loaded), HostFunctions::default(), None, None) + .unwrap(); } // Snapshot lineage and restore semantics. `restore` accepts any @@ -2900,7 +2920,7 @@ fn separate_oci_loads_are_mutually_restore_compatible() { let s_y = Arc::new(Snapshot::checked_load(&path, OciTag::new("v1").unwrap()).unwrap()); let mut sbox_x = - MultiUseSandbox::from_snapshot(s_x.clone(), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(s_x.clone(), HostFunctions::default(), None, None).unwrap(); sbox_x.restore(s_y.clone()).unwrap(); assert_eq!(sbox_x.call::("GetStatic", ()).unwrap(), 0); @@ -2920,7 +2940,8 @@ fn oci_loaded_snapshot_supports_full_lifecycle() { let loaded = Arc::new(Snapshot::checked_load(&path, OciTag::new("v1").unwrap()).unwrap()); let mut sbox = - MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(loaded.clone(), HostFunctions::default(), None, None) + .unwrap(); sbox.call::("AddToStatic", 1i32).unwrap(); let s1 = sbox.snapshot().unwrap(); @@ -3117,7 +3138,8 @@ fn save_new_tag_into_loaded_layout_preserves_live_mapping() { // A sandbox built on the live mapping still restores cleanly. let mut live = - MultiUseSandbox::from_snapshot(loaded_a.clone(), HostFunctions::default(), None).unwrap(); + MultiUseSandbox::from_snapshot(loaded_a.clone(), HostFunctions::default(), None, None) + .unwrap(); live.call::("AddToStatic", 7i32).unwrap(); assert_eq!(live.call::("GetStatic", ()).unwrap(), 7); live.restore(loaded_a).unwrap(); @@ -3170,9 +3192,13 @@ fn from_snapshot_silently_ignores_layout_overrides() { config.set_heap_size((original_heap as u64) * 2); config.set_scratch_size(original_scratch * 2); - let mut sbox2 = - MultiUseSandbox::from_snapshot(snapshot.clone(), HostFunctions::default(), Some(config)) - .unwrap(); + let mut sbox2 = MultiUseSandbox::from_snapshot( + snapshot.clone(), + HostFunctions::default(), + Some(config), + None, + ) + .unwrap(); sbox2.call::("GetStatic", ()).unwrap(); @@ -3197,7 +3223,8 @@ fn from_snapshot_honors_guest_core_dump_enabled() { config.set_guest_core_dump(true); let mut sbox2 = - MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), Some(config)).unwrap(); + MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), Some(config), None) + .unwrap(); let dir = tempfile::tempdir().unwrap(); sbox2 @@ -3228,7 +3255,8 @@ fn from_snapshot_honors_guest_core_dump_disabled() { config.set_guest_core_dump(false); let mut sbox2 = - MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), Some(config)).unwrap(); + MultiUseSandbox::from_snapshot(snapshot, HostFunctions::default(), Some(config), None) + .unwrap(); let dir = tempfile::tempdir().unwrap(); sbox2 diff --git a/src/hyperlight_host/tests/snapshot_goldens/checks.rs b/src/hyperlight_host/tests/snapshot_goldens/checks.rs index b7c3c79df..06bc2769b 100644 --- a/src/hyperlight_host/tests/snapshot_goldens/checks.rs +++ b/src/hyperlight_host/tests/snapshot_goldens/checks.rs @@ -61,7 +61,7 @@ impl<'a> GoldenTest<'a> { .map_err(|e| format!("Snapshot::checked_load({}): {e}", self.tag()))?; let mut funcs = HostFunctions::default(); register_host_echo_fns(&mut funcs); - MultiUseSandbox::from_snapshot(Arc::new(snap), funcs, None) + MultiUseSandbox::from_snapshot(Arc::new(snap), funcs, None, None) .map_err(|e| format!("MultiUseSandbox::from_snapshot({}): {e}", self.tag())) } } @@ -308,7 +308,7 @@ fn chained_snapshot(golden: &GoldenTest) -> Result<(), String> { let loaded = Snapshot::checked_load(&layout, tag).map_err(|e| format!("checked_load: {e}"))?; let mut funcs = HostFunctions::default(); register_host_echo_fns(&mut funcs); - let mut sbox2 = MultiUseSandbox::from_snapshot(Arc::new(loaded), funcs, None) + let mut sbox2 = MultiUseSandbox::from_snapshot(Arc::new(loaded), funcs, None, None) .map_err(|e| format!("from_snapshot: {e}"))?; let val: i32 = sbox2 .call("GetStatic", ())