diff --git a/CHANGELOG.md b/CHANGELOG.md index 56a89482b..ce2cdb70f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). resets to a clean default. On KVM the guest may only read or write declared MSRs, on MSHV and WHP this is not enforced. by @ludfjig in https://github.com/hyperlight-dev/hyperlight/pull/991 * **Breaking:** Filesystem paths are now represented using `PathBuf`. `GuestBinary::FilePath` now stores a `PathBuf` instead of a `String`, and `MultiUseSandbox::generate_crashdump_to_dir` accepts `Into` instead of `Into`. Callers passing a `String` to `GuestBinary::FilePath` must convert it using `.into()`. +* **Breaking:** `GuestBinary::Buffer` owns its bytes as a `Vec`, so `GuestBinary` no longer borrows and carries no lifetime parameter. * Deprecate `MultiUseSandbox::poisoned` in favor of `MultiUseSandbox::status().is_poisoned()`. Certain fixed guest addresses were changed on AArch64 to more easily diff --git a/README.md b/README.md index f8942fd6d..0cab5b250 100644 --- a/README.md +++ b/README.md @@ -24,9 +24,9 @@ Hyperlight lets you safely run untrusted code inside hypervisor-isolated micro V // Build a sandbox from a guest binary, registering a host function the guest // can call. In a real app that function might query a database, read a config, // or call an external API. By default, guests can only print to the host. -let mut sandbox = SandboxBuilder::new() +let mut sandbox = SandboxBuilder::from_file(guest_path) .host_function("GetWeekday", || Ok("Monday".to_string())) - .build_from_file(guest_path)?; + .build()?; // Call a function inside the VM let greeting: String = sandbox.call("SayHello", "World".to_string())?; diff --git a/docs/how-to-debug-a-hyperlight-guest.md b/docs/how-to-debug-a-hyperlight-guest.md index 298305313..52a1c8ba4 100644 --- a/docs/how-to-debug-a-hyperlight-guest.md +++ b/docs/how-to-debug-a-hyperlight-guest.md @@ -222,9 +222,9 @@ The name and location of the dump file will be printed to the console and logged **NOTE**: By enabling the `crashdump` feature, you instruct Hyperlight to create core dump files for all sandboxes when an unhandled crash occurs. To selectively disable this feature for a specific sandbox, call `guest_core_dump(false)` on the `SandboxBuilder`. ```rust - let sandbox = SandboxBuilder::new() + let sandbox = SandboxBuilder::from_file(guest_path) .guest_core_dump(false) // Disable core dump for this sandbox - .build_from_file(guest_path)?; + .build()?; ``` ## Creating a dump on demand diff --git a/fuzz/fuzz_targets/guest_call.rs b/fuzz/fuzz_targets/guest_call.rs index e1e37e278..25e270b89 100644 --- a/fuzz/fuzz_targets/guest_call.rs +++ b/fuzz/fuzz_targets/guest_call.rs @@ -15,8 +15,8 @@ static SANDBOX: OnceLock> = OnceLock::new(); // For fuzzing efficiency, we create one Sandbox and reuse it for all fuzzing iterations. fuzz_target!( init: { - let mu_sbox = SandboxBuilder::new() - .build_from_file(simple_guest_for_fuzzing_as_pathbuf()) + let mu_sbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf()) + .build() .unwrap(); SANDBOX.set(Mutex::new(mu_sbox)).unwrap(); }, diff --git a/fuzz/fuzz_targets/guest_trace.rs b/fuzz/fuzz_targets/guest_trace.rs index c39df9a48..9cb05050f 100644 --- a/fuzz/fuzz_targets/guest_trace.rs +++ b/fuzz/fuzz_targets/guest_trace.rs @@ -54,9 +54,9 @@ impl<'a> Arbitrary<'a> for FuzzInput { fuzz_target!( init: { // In local tests, 256 KiB seemed sufficient for deep recursion - let mu_sbox = SandboxBuilder::new() + let mu_sbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf()) .scratch_size(256 * 1024) - .build_from_file(simple_guest_for_fuzzing_as_pathbuf()) + .build() .unwrap(); SANDBOX.set(Mutex::new(mu_sbox)).unwrap(); diff --git a/fuzz/fuzz_targets/host_call.rs b/fuzz/fuzz_targets/host_call.rs index 82ecdce5b..6dba3ec46 100644 --- a/fuzz/fuzz_targets/host_call.rs +++ b/fuzz/fuzz_targets/host_call.rs @@ -17,11 +17,11 @@ static SANDBOX: OnceLock> = OnceLock::new(); // For fuzzing efficiency, we create one Sandbox and reuse it for all fuzzing iterations. fuzz_target!( init: { - let mu_sbox = SandboxBuilder::new() + let mu_sbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf()) .output_data_size(64 * 1024) // 64 KB output buffer .input_data_size(64 * 1024) // 64 KB input buffer .scratch_size(512 * 1024) // large scratch region to contain those buffers, any data copies, etc. - .build_from_file(simple_guest_for_fuzzing_as_pathbuf()) + .build() .unwrap(); SANDBOX.set(Mutex::new(mu_sbox)).unwrap(); }, diff --git a/fuzz/fuzz_targets/host_print.rs b/fuzz/fuzz_targets/host_print.rs index 89ccc1dfc..faf389e68 100644 --- a/fuzz/fuzz_targets/host_print.rs +++ b/fuzz/fuzz_targets/host_print.rs @@ -14,8 +14,8 @@ static SANDBOX: OnceLock> = OnceLock::new(); // For fuzzing efficiency, we create one Sandbox and reuse it for all fuzzing iterations. fuzz_target!( init: { - let mu_sbox = SandboxBuilder::new() - .build_from_file(simple_guest_for_fuzzing_as_pathbuf()) + let mu_sbox = SandboxBuilder::from_file(simple_guest_for_fuzzing_as_pathbuf()) + .build() .unwrap(); SANDBOX.set(Mutex::new(mu_sbox)).unwrap(); }, diff --git a/src/hyperlight_host/benches/benchmarks.rs b/src/hyperlight_host/benches/benchmarks.rs index befe46fa3..70df4384a 100644 --- a/src/hyperlight_host/benches/benchmarks.rs +++ b/src/hyperlight_host/benches/benchmarks.rs @@ -31,9 +31,9 @@ enum SandboxSize { } impl SandboxSize { - /// Returns a builder configured for this sandbox size. + /// Returns a builder for the simple guest, configured for this sandbox size. fn builder(&self) -> SandboxBuilder { - let builder = SandboxBuilder::new(); + let builder = SandboxBuilder::from_file(simple_guest_as_pathbuf()); match self { Self::Default => builder, Self::Small => builder.heap_size(SMALL_HEAP_SIZE), @@ -59,9 +59,7 @@ impl SandboxSize { } fn create_multiuse_sandbox_with_size(size: SandboxSize) -> MultiUseSandbox { - size.builder() - .build_from_file(simple_guest_as_pathbuf()) - .unwrap() + size.builder().build().unwrap() } // ============================================================================ @@ -132,7 +130,7 @@ fn bench_guest_call_with_host_function(b: &mut criterion::Bencher, size: Sandbox let mut multiuse_sandbox = size .builder() .host_function("HostAdd", |a: i32, b: i32| Ok(a + b)) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); b.iter(|| { @@ -352,13 +350,13 @@ fn guest_call_benchmark_large_param(c: &mut Criterion) { let large_vec = vec![0u8; SIZE]; let large_string = String::from_utf8(large_vec.clone()).unwrap(); - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) // 2 * SIZE + 1 MB, to allow 1MB for the rest of the serialized function call .input_data_size(2 * SIZE + (1024 * 1024)) .heap_size(SIZE as u64 * 15) // Big enough for the IO data regions and enough of the heap to be used .scratch_size(6 * SIZE + 4 * (1024 * 1024)) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); b.iter_with_setup( @@ -434,9 +432,9 @@ fn sample_workloads_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("sample_workloads"); fn bench_24k_in_8k_out(b: &mut criterion::Bencher, guest_path: std::path::PathBuf) { - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(guest_path) .input_data_size(25 * 1024) - .build_from_file(guest_path) + .build() .unwrap(); b.iter_with_setup( diff --git a/src/hyperlight_host/examples/crashdump/main.rs b/src/hyperlight_host/examples/crashdump/main.rs index fbd7eddcc..5ff0a895e 100644 --- a/src/hyperlight_host/examples/crashdump/main.rs +++ b/src/hyperlight_host/examples/crashdump/main.rs @@ -126,7 +126,7 @@ fn main() -> hyperlight_host::Result<()> { /// 4. The crash dump is written automatically (no explicit call needed) #[cfg(all(crashdump, target_os = "linux"))] fn guest_crash_auto_dump(guest_path: &Path) -> hyperlight_host::Result<()> { - let mut sandbox = SandboxBuilder::new().build_from_file(guest_path)?; + let mut sandbox = SandboxBuilder::from_file(guest_path).build()?; // Map a file as read-only into the guest at a known address. let mapping_file = create_mapping_file(); @@ -186,7 +186,7 @@ fn create_mapping_file() -> std::path::PathBuf { /// fault), the automatic crash dump code in the VM run loop is not reached. /// To get a crash dump in this case, call `generate_crashdump()` explicitly. fn guest_crash_with_on_demand_dump(guest_path: &Path) -> hyperlight_host::Result<()> { - let mut sandbox = SandboxBuilder::new().build_from_file(guest_path)?; + let mut sandbox = SandboxBuilder::from_file(guest_path).build()?; // This call triggers a ud2 instruction in the guest. The guest's IDT // catches the #UD exception and reports it back to the host as a @@ -224,9 +224,9 @@ fn guest_crash_with_on_demand_dump(guest_path: &Path) -> hyperlight_host::Result fn guest_crash_with_dump_disabled(guest_path: &Path) -> hyperlight_host::Result<()> { println!("Core dump disabled for this sandbox."); - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(guest_path) .guest_core_dump(false) - .build_from_file(guest_path)?; + .build()?; let mapping_file = create_mapping_file(); let guest_base: u64 = 0x200000000; @@ -360,7 +360,7 @@ mod tests { // Create sandbox with default config (crashdump enabled) let guest_path = hyperlight_testing::simple_guest_as_pathbuf(); - let mut sbox = SandboxBuilder::new().build_from_file(guest_path).unwrap(); + let mut sbox = SandboxBuilder::from_file(guest_path).build().unwrap(); // Map an additional test file into the guest at a known address. // The core dump already includes snapshot and scratch regions @@ -429,16 +429,16 @@ mod tests { /// sandboxes resolve symbols the same way as directly-evolved ones. fn generate_crashdump_from_snapshot(dump_dir: &Path) -> PathBuf { let guest_path = hyperlight_testing::simple_guest_as_pathbuf(); - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_file(guest_path) .guest_core_dump(true) - .build_from_file(guest_path) + .build() .unwrap(); let snapshot = sbox.snapshot().expect("snapshot"); - let mut sbox2 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(snapshot) .guest_core_dump(true) - .build_from_snapshot(snapshot) + .build() .unwrap(); let result = sbox2.call::<()>("TriggerException", ()); diff --git a/src/hyperlight_host/examples/func_ctx/main.rs b/src/hyperlight_host/examples/func_ctx/main.rs index 0199b4745..65169def9 100644 --- a/src/hyperlight_host/examples/func_ctx/main.rs +++ b/src/hyperlight_host/examples/func_ctx/main.rs @@ -8,7 +8,7 @@ fn main() { // create a new `MultiUseSandbox` configured to run the `simpleguest.exe` // test guest binary let path = simple_guest_as_pathbuf(); - let mut sbox = SandboxBuilder::new().build_from_file(path).unwrap(); + let mut sbox = SandboxBuilder::from_file(path).build().unwrap(); // Do several calls against a sandbox running the `simpleguest.exe` binary, // and print their results diff --git a/src/hyperlight_host/examples/guest-debugging/main.rs b/src/hyperlight_host/examples/guest-debugging/main.rs index 847e7186a..47a493a9b 100644 --- a/src/hyperlight_host/examples/guest-debugging/main.rs +++ b/src/hyperlight_host/examples/guest-debugging/main.rs @@ -8,7 +8,7 @@ use hyperlight_host::sandbox::config::DebugInfo; /// Build a sandbox builder that enables GDB debugging when the `gdb` feature is enabled. fn debuggable_builder() -> SandboxBuilder { - let builder = SandboxBuilder::new(); + let builder = SandboxBuilder::from_file(hyperlight_testing::simple_guest_as_pathbuf()); #[cfg(gdb)] let builder = builder.guest_debug_info(DebugInfo { port: 8080 }); @@ -26,12 +26,13 @@ fn main() -> hyperlight_host::Result<()> { // Build a sandbox with a guest binary and debug enabled let mut multi_use_sandbox_dbg = debuggable_builder() .host_function("Sleep5Secs", sleep_5_secs) - .build_from_file(hyperlight_testing::simple_guest_as_pathbuf())?; + .build()?; // Build a sandbox with a guest binary - let mut multi_use_sandbox = SandboxBuilder::new() - .host_function("Sleep5Secs", sleep_5_secs) - .build_from_file(hyperlight_testing::simple_guest_as_pathbuf())?; + let mut multi_use_sandbox = + SandboxBuilder::from_file(hyperlight_testing::simple_guest_as_pathbuf()) + .host_function("Sleep5Secs", sleep_5_secs) + .build()?; // Call guest function multi_use_sandbox_dbg @@ -338,8 +339,8 @@ mod tests { let (out_file_path, cmd_file_path, manifest_dir) = gdb_test_paths("gdb-from-snapshot"); // Build a sandbox the normal way and snapshot it in-memory. - let mut producer = SandboxBuilder::new() - .build_from_file(hyperlight_testing::simple_guest_as_pathbuf()) + let mut producer = SandboxBuilder::from_file(hyperlight_testing::simple_guest_as_pathbuf()) + .build() .unwrap(); let snap = producer.snapshot().unwrap(); @@ -353,9 +354,9 @@ mod tests { // here before the client is launched below. let snap_thread = snap.clone(); let sandbox_thread = thread::spawn(move || -> Result<()> { - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_snapshot(snap_thread) .guest_debug_info(DebugInfo { port: PORT }) - .build_from_snapshot(snap_thread)?; + .build()?; sbox.call::( "PrintOutput", "Hello from a from_snapshot sandbox\n".to_string(), diff --git a/src/hyperlight_host/examples/hello-world/main.rs b/src/hyperlight_host/examples/hello-world/main.rs index 2b3566400..c1e5744c8 100644 --- a/src/hyperlight_host/examples/hello-world/main.rs +++ b/src/hyperlight_host/examples/hello-world/main.rs @@ -7,12 +7,12 @@ use hyperlight_host::SandboxBuilder; fn main() -> hyperlight_host::Result<()> { // Build a sandbox running a guest binary, with a host function registered. // Note: the host function is unused, it's just here for demonstration purposes - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(hyperlight_testing::simple_guest_as_pathbuf()) .host_function("Sleep5Secs", || { thread::sleep(std::time::Duration::from_secs(5)); Ok(()) }) - .build_from_file(hyperlight_testing::simple_guest_as_pathbuf())?; + .build()?; // Call guest function let message = "Hello, World! I am executing inside of a VM :)\n".to_string(); diff --git a/src/hyperlight_host/examples/logging/main.rs b/src/hyperlight_host/examples/logging/main.rs index c6ca53dbb..c6446802b 100644 --- a/src/hyperlight_host/examples/logging/main.rs +++ b/src/hyperlight_host/examples/logging/main.rs @@ -25,9 +25,9 @@ fn main() -> Result<()> { let path = hyperlight_guest_path.clone(); let res: Result<()> = { // Create a new sandbox. - let mut multiuse_sandbox = SandboxBuilder::new() + let mut multiuse_sandbox = SandboxBuilder::from_file(path) .host_print(fn_writer) - .build_from_file(path)?; + .build()?; // Call a guest function 5 times to generate some log entries. for _ in 0..5 { @@ -53,8 +53,7 @@ fn main() -> Result<()> { } // Create a new sandbox. - let mut multiuse_sandbox = - SandboxBuilder::new().build_from_file(hyperlight_guest_path.clone())?; + let mut multiuse_sandbox = SandboxBuilder::from_file(hyperlight_guest_path.clone()).build()?; let interrupt_handle = multiuse_sandbox.interrupt_handle(); let barrier = Arc::new(Barrier::new(2)); let barrier2 = barrier.clone(); diff --git a/src/hyperlight_host/examples/map-file-cow-test/main.rs b/src/hyperlight_host/examples/map-file-cow-test/main.rs index 9f4a4dd46..7a7930acb 100644 --- a/src/hyperlight_host/examples/map-file-cow-test/main.rs +++ b/src/hyperlight_host/examples/map-file-cow-test/main.rs @@ -20,11 +20,11 @@ use std::path::Path; use hyperlight_host::SandboxBuilder; fn run_once(test_file: &Path, label: &str) -> hyperlight_host::Result<()> { - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(hyperlight_testing::simple_guest_as_pathbuf()) .heap_size(4 * 1024 * 1024) .scratch_size(64 * 1024 * 1024) .mapped_file_cow(test_file, 0xC000_0000) - .build_from_file(hyperlight_testing::simple_guest_as_pathbuf())?; + .build()?; eprintln!( "[{label}] sandbox built with a {} byte file mapped", std::fs::metadata(test_file)?.len() diff --git a/src/hyperlight_host/examples/metrics/main.rs b/src/hyperlight_host/examples/metrics/main.rs index 0d1480645..9b1084576 100644 --- a/src/hyperlight_host/examples/metrics/main.rs +++ b/src/hyperlight_host/examples/metrics/main.rs @@ -36,9 +36,9 @@ fn do_hyperlight_stuff() { let path = hyperlight_guest_path.clone(); let handle = spawn(move || -> Result<()> { // Create a new sandbox. - let mut multiuse_sandbox = SandboxBuilder::new() + let mut multiuse_sandbox = SandboxBuilder::from_file(path) .host_print(fn_writer) - .build_from_file(path)?; + .build()?; // Call a guest function 5 times to generate some metrics. for _ in 0..5 { @@ -64,8 +64,8 @@ fn do_hyperlight_stuff() { } // Create a new sandbox. - let mut multiuse_sandbox = SandboxBuilder::new() - .build_from_file(hyperlight_guest_path.clone()) + let mut multiuse_sandbox = SandboxBuilder::from_file(hyperlight_guest_path.clone()) + .build() .expect("Failed to build sandbox"); let interrupt_handle = multiuse_sandbox.interrupt_handle(); diff --git a/src/hyperlight_host/examples/tracing-chrome/main.rs b/src/hyperlight_host/examples/tracing-chrome/main.rs index 7f4183d8d..b7842e295 100644 --- a/src/hyperlight_host/examples/tracing-chrome/main.rs +++ b/src/hyperlight_host/examples/tracing-chrome/main.rs @@ -14,7 +14,7 @@ fn main() -> Result<()> { let simple_guest_path = simple_guest_as_pathbuf(); // Create a new sandbox. - let mut sbox = SandboxBuilder::new().build_from_file(simple_guest_path)?; + let mut sbox = SandboxBuilder::from_file(simple_guest_path).build()?; // do the function call let current_time = std::time::Instant::now(); diff --git a/src/hyperlight_host/examples/tracing-otlp/main.rs b/src/hyperlight_host/examples/tracing-otlp/main.rs index d8f1e6125..3b5a7e1d2 100644 --- a/src/hyperlight_host/examples/tracing-otlp/main.rs +++ b/src/hyperlight_host/examples/tracing-otlp/main.rs @@ -109,9 +109,9 @@ fn run_example(wait_input: bool) -> HyperlightResult<()> { let _entered = span.enter(); // Create a new sandbox. - let mut multiuse_sandbox = SandboxBuilder::new() + let mut multiuse_sandbox = SandboxBuilder::from_file(path.clone()) .host_print(fn_writer) - .build_from_file(path.clone())?; + .build()?; // Call a guest function 5 times to generate some log entries. for _ in 0..5 { diff --git a/src/hyperlight_host/examples/tracing/main.rs b/src/hyperlight_host/examples/tracing/main.rs index dfd2ba5e1..941c3ec9d 100644 --- a/src/hyperlight_host/examples/tracing/main.rs +++ b/src/hyperlight_host/examples/tracing/main.rs @@ -53,9 +53,9 @@ fn run_example() -> Result<()> { let _entered = span.enter(); // Create a new sandbox. - let mut multiuse_sandbox = SandboxBuilder::new() + let mut multiuse_sandbox = SandboxBuilder::from_file(path) .host_print(fn_writer) - .build_from_file(path)?; + .build()?; // Call a guest function 5 times to generate some log entries. for _ in 0..5 { @@ -80,8 +80,7 @@ fn run_example() -> Result<()> { } // Create a new sandbox. - let mut multiuse_sandbox = - SandboxBuilder::new().build_from_file(hyperlight_guest_path.clone())?; + let mut multiuse_sandbox = SandboxBuilder::from_file(hyperlight_guest_path.clone()).build()?; let interrupt_handle = multiuse_sandbox.interrupt_handle(); // Call a function that gets cancelled by the host function 5 times to generate some log entries. diff --git a/src/hyperlight_host/src/mem/elf.rs b/src/hyperlight_host/src/mem/elf.rs index f6cde2906..cbd1485d8 100644 --- a/src/hyperlight_host/src/mem/elf.rs +++ b/src/hyperlight_host/src/mem/elf.rs @@ -97,8 +97,9 @@ impl framehop::ModuleSectionInfo> for &UnwindInfo { } impl ElfInfo { - pub(crate) fn new(bytes: &[u8]) -> Result { - let elf = Elf::parse(bytes)?; + pub(crate) fn new(bytes: impl Into>) -> Result { + let bytes = bytes.into(); + let mut elf = Elf::parse(&bytes)?; let relocs = elf.dynrels.iter().chain(elf.dynrelas.iter()).collect(); if !elf .program_headers @@ -110,25 +111,32 @@ impl ElfInfo { // Look for the hyperlight version note embedded by // hyperlight-guest-bin. - let guest_bin_version = Self::read_version_note(&elf, bytes); + let guest_bin_version = Self::read_version_note(&elf, &bytes); + + let phdrs = std::mem::take(&mut elf.program_headers); + let entry = elf.entry; + #[cfg(feature = "mem_profile")] + let shdrs = elf + .section_headers + .iter() + .filter_map(|sh| { + Some(ResolvedSectionHeader { + name: elf.shdr_strtab.get_at(sh.sh_name)?.to_string(), + addr: sh.sh_addr, + offset: sh.sh_offset, + size: sh.sh_size, + }) + }) + .collect(); + + drop(elf); Ok(ElfInfo { - payload: bytes.to_vec(), - phdrs: elf.program_headers, + payload: bytes, + phdrs, #[cfg(feature = "mem_profile")] - shdrs: elf - .section_headers - .iter() - .filter_map(|sh| { - Some(ResolvedSectionHeader { - name: elf.shdr_strtab.get_at(sh.sh_name)?.to_string(), - addr: sh.sh_addr, - offset: sh.sh_offset, - size: sh.sh_size, - }) - }) - .collect(), - entry: elf.entry, + shdrs, + entry, relocs, guest_bin_version, }) diff --git a/src/hyperlight_host/src/mem/exe.rs b/src/hyperlight_host/src/mem/exe.rs index 7bf3a446d..7adab7458 100644 --- a/src/hyperlight_host/src/mem/exe.rs +++ b/src/hyperlight_host/src/mem/exe.rs @@ -66,9 +66,9 @@ impl ExeInfo { let mut file = File::open(path)?; let mut contents = Vec::new(); file.read_to_end(&mut contents)?; - Self::from_buf(&contents) + Self::from_buf(contents) } - pub fn from_buf(buf: &[u8]) -> Result { + pub fn from_buf(buf: impl Into>) -> Result { ElfInfo::new(buf).map(ExeInfo::Elf) } pub fn entrypoint(&self) -> Offset { @@ -187,7 +187,7 @@ mod tests { fn patched_version_reports_mismatch() { let bytes = simpleguest_with_patched_version(); - let info = ExeInfo::from_buf(&bytes).expect("failed to load patched ELF"); + let info = ExeInfo::from_buf(bytes).expect("failed to load patched ELF"); assert_eq!(info.guest_bin_version(), Some("0.0.0")); assert_ne!( info.guest_bin_version().unwrap(), @@ -217,7 +217,7 @@ mod tests { let bytes = simpleguest_with_patched_version(); let result = crate::sandbox::snapshot::Snapshot::from_env( - crate::GuestBinary::Buffer(&bytes), + crate::GuestBinary::Buffer(bytes), crate::sandbox::SandboxConfiguration::default(), ); diff --git a/src/hyperlight_host/src/metrics/mod.rs b/src/hyperlight_host/src/metrics/mod.rs index 015e159e5..5d6924ba3 100644 --- a/src/hyperlight_host/src/metrics/mod.rs +++ b/src/hyperlight_host/src/metrics/mod.rs @@ -93,8 +93,8 @@ mod tests { let recorder = metrics_util::debugging::DebuggingRecorder::new(); let snapshotter = recorder.snapshotter(); let snapshot = with_local_recorder(&recorder, || { - let mut multi = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut multi = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let interrupt_handle = multi.interrupt_handle(); diff --git a/src/hyperlight_host/src/sandbox/builder.rs b/src/hyperlight_host/src/sandbox/builder.rs index e803a61eb..7ac5a4412 100644 --- a/src/hyperlight_host/src/sandbox/builder.rs +++ b/src/hyperlight_host/src/sandbox/builder.rs @@ -23,12 +23,31 @@ use crate::{ GuestBinary, HostFunctions, MultiUseSandbox as Sandbox, Result, UninitializedSandbox, new_error, }; +/// What a [`SandboxBuilder`] builds the sandbox from. +enum Source { + GuestBinary(GuestBinary), + Snapshot(Arc), +} + +impl Source { + fn file(path: impl AsRef) -> Self { + Self::GuestBinary(GuestBinary::FilePath(path.as_ref().to_path_buf())) + } + + fn bytes(buffer: impl Into>) -> Self { + Self::GuestBinary(GuestBinary::Buffer(buffer.into())) + } +} + /// Builds a [`Sandbox`]. /// -/// Start from [`SandboxBuilder::new`], chain the settings you need, then call -/// one of the `build_from_*` methods to create the sandbox from a guest binary -/// on disk, a guest binary in memory, or a [`Snapshot`]. Every setting has a -/// default, so a builder with no adjustments is valid. +/// Start from [`SandboxBuilder::from_file`], +/// [`SandboxBuilder::from_bytes`] or [`SandboxBuilder::from_snapshot`], +/// chain the settings you need, then call [`SandboxBuilder::build`]. Every +/// setting has a default, so a builder with no adjustments is valid. +/// +/// By default only the `HostPrint` host function is registered, which writes +/// guest output to the host's stdout. Replace it with [`Self::host_print`]. /// /// # Examples /// @@ -37,10 +56,10 @@ use crate::{ /// ```no_run /// # use hyperlight_host::{Result, SandboxBuilder}; /// # fn example() -> Result<()> { -/// let mut sandbox = SandboxBuilder::new() +/// let mut sandbox = SandboxBuilder::from_file("guest.bin") /// .heap_size(1024 * 1024) /// .host_function("Add", |a: i32, b: i32| a + b) -/// .build_from_file("guest.bin")?; +/// .build()?; /// /// let result: String = sandbox.call("Echo", "hello".to_string())?; /// # Ok(()) @@ -54,21 +73,21 @@ use crate::{ /// ```no_run /// # use hyperlight_host::{Result, SandboxBuilder}; /// # fn example() -> Result<()> { -/// let mut sandbox = SandboxBuilder::new() +/// let mut sandbox = SandboxBuilder::from_file("guest.bin") /// .host_function("Add", |a: i32, b: i32| a + b) -/// .build_from_file("guest.bin")?; +/// .build()?; /// let snapshot = sandbox.snapshot()?; /// -/// let mut restored = SandboxBuilder::new() +/// let mut restored = SandboxBuilder::from_snapshot(snapshot) /// .host_function("Add", |a: i32, b: i32| a + b) -/// .build_from_snapshot(snapshot)?; +/// .build()?; /// /// let result: String = restored.call("Echo", "hello".to_string())?; /// # Ok(()) /// # } /// ``` -#[derive(Default)] pub struct SandboxBuilder { + source: Source, cfg: SandboxConfiguration, host_funcs: HostFunctions, init_data: Option<(Vec, MemoryRegionFlags)>, @@ -78,88 +97,100 @@ pub struct SandboxBuilder { } impl SandboxBuilder { - /// Create a builder with the default configuration and the default host - /// functions. - /// - /// By default only the `HostPrint` host function is registered, which - /// writes guest output to the host's stdout. Replace it with - /// [`Self::host_print`]. - pub fn new() -> Self { - Self::default() + fn with_source(source: Source) -> Self { + Self { + source, + cfg: SandboxConfiguration::default(), + host_funcs: HostFunctions::default(), + init_data: None, + mapped_file_cow: Vec::new(), + mapped_memory_regions: Vec::new(), + guest_log_level: None, + } } - /// Build a sandbox running the guest binary at `path`. - pub fn build_from_file(self, path: impl AsRef) -> Result { - let path = path.as_ref().to_path_buf(); - self.build_from_guest_binary(GuestBinary::FilePath(path)) + /// Build a sandbox running the guest binary at `path`, an ELF file. + pub fn from_file(path: impl AsRef) -> Self { + Self::with_source(Source::file(path)) } - /// Build a sandbox running the guest binary held in `buffer`. - pub fn build_from_bytes(self, buffer: impl AsRef<[u8]>) -> Result { - let buffer = buffer.as_ref(); - self.build_from_guest_binary(GuestBinary::Buffer(buffer)) + /// Build a sandbox running the guest binary held in `buffer`, the contents + /// of an ELF file. + pub fn from_bytes(buffer: impl Into>) -> Self { + Self::with_source(Source::bytes(buffer)) } - fn build_from_guest_binary(self, guest_binary: GuestBinary) -> Result { - let init_data = self.init_data.as_ref().map(|(data, flags)| GuestBlob { - data, - permissions: *flags, - }); - - let env = GuestEnvironment { - init_data, - guest_binary, - }; - - let mut uninitialized_sandbox = UninitializedSandbox::new(env, Some(self.cfg))?; - - uninitialized_sandbox.host_funcs = Arc::new(Mutex::new(self.host_funcs.into_inner())); - - for (path, guest_base) in self.mapped_file_cow { - uninitialized_sandbox.map_file_cow(&path, guest_base)?; - } - - if let Some(log_level) = self.guest_log_level { - uninitialized_sandbox.set_max_guest_log_level(log_level); - } - - let mut sandbox = uninitialized_sandbox.evolve()?; - - for region in self.mapped_memory_regions { - // SAFETY: the caller of `mapped_memory_region` guaranteed each region - // stays valid and unmodified for the lifetime of this sandbox. - unsafe { sandbox.map_region(®ion)? }; - } - - Ok(sandbox) + /// Build a sandbox restoring the guest from `snapshot`. + pub fn from_snapshot(snapshot: Arc) -> Self { + Self::with_source(Source::Snapshot(snapshot)) } - /// Build a sandbox restored from `snapshot`. + /// Create the sandbox. /// /// # Errors /// - /// Returns an error if [`Self::init_data`] or [`Self::guest_log_level`] - /// are set. The snapshot already carries both, so they have no effect here. - pub fn build_from_snapshot(self, snapshot: Arc) -> Result { - if self.init_data.is_some() { - return Err(new_error!( - "init_data has no effect when building from a snapshot, as the snapshot already contains it" - )); - } - - if self.guest_log_level.is_some() { - return Err(new_error!( - "guest_log_level has no effect when building from a snapshot, as the snapshot already contains it" - )); - } - - let mut sandbox = Sandbox::from_snapshot(snapshot, self.host_funcs, Some(self.cfg))?; - - for (path, guest_base) in self.mapped_file_cow { - sandbox.map_file_cow(&path, guest_base)?; - } + /// When building from a snapshot, returns an error if [`Self::init_data`] + /// or [`Self::guest_log_level`] are set. The snapshot already carries + /// both, so they have no effect there. + pub fn build(self) -> Result { + let Self { + source, + cfg, + host_funcs, + init_data, + mapped_file_cow, + mapped_memory_regions, + guest_log_level, + } = self; + + let mut sandbox = match source { + Source::GuestBinary(guest_binary) => { + let env = GuestEnvironment { + init_data: init_data.as_ref().map(|(data, flags)| GuestBlob { + data, + permissions: *flags, + }), + guest_binary, + }; + + let mut uninitialized_sandbox = UninitializedSandbox::new(env, Some(cfg))?; + + uninitialized_sandbox.host_funcs = Arc::new(Mutex::new(host_funcs.into_inner())); + + for (path, guest_base) in mapped_file_cow { + uninitialized_sandbox.map_file_cow(&path, guest_base)?; + } + + if let Some(log_level) = guest_log_level { + uninitialized_sandbox.set_max_guest_log_level(log_level); + } + + uninitialized_sandbox.evolve()? + } + Source::Snapshot(snapshot) => { + if init_data.is_some() { + return Err(new_error!( + "init_data has no effect when building from a snapshot, as the snapshot already contains it" + )); + } + + if guest_log_level.is_some() { + return Err(new_error!( + "guest_log_level has no effect when building from a snapshot, as the snapshot already contains it" + )); + } + + let mut sandbox = Sandbox::from_snapshot(snapshot, host_funcs, Some(cfg))?; + + for (path, guest_base) in mapped_file_cow { + sandbox.map_file_cow(&path, guest_base)?; + } + + sandbox + } + }; - for region in self.mapped_memory_regions { + for region in mapped_memory_regions { // SAFETY: the caller of `mapped_memory_region` guaranteed each region // stays valid and unmodified for the lifetime of this sandbox. unsafe { sandbox.map_region(®ion)? }; @@ -173,8 +204,8 @@ impl SandboxBuilder { /// Sets the sandbox `init_data` into the sandbox's memory when it is built, with `flags` as /// the guest's permissions on that region. /// - /// Note: [`Self::build_from_snapshot`] errors if this setting is set, as the snapshot already - /// contains the init data. + /// Note: [`Self::build`] errors if this setting is set and the builder's + /// source is a snapshot, as the snapshot already contains the init data. pub fn init_data(mut self, data: impl Into>, flags: MemoryRegionFlags) -> Self { self.init_data = Some((data.into(), flags)); self @@ -184,8 +215,8 @@ impl SandboxBuilder { /// copy-on-write. /// /// `guest_base` must be page-aligned and lie outside the sandbox's primary - /// shared memory region. Violations surface as an error from the - /// `build_from_*` call, not here. Call this once per file to map several. + /// shared memory region. Violations surface as an error from + /// [`Self::build`], not here. Call this once per file to map several. pub fn mapped_file_cow(mut self, path: impl AsRef, guest_base: u64) -> Self { self.mapped_file_cow .push((path.as_ref().to_path_buf(), guest_base)); @@ -212,8 +243,8 @@ impl SandboxBuilder { /// If not set, the log level is determined by the `RUST_LOG` environment variable, /// defaulting to [`LevelFilter::ERROR`] if unset. /// - /// Note: [`Self::build_from_snapshot`] errors if this setting is set, as the log level is - /// already captured in the snapshot. + /// Note: [`Self::build`] errors if this setting is set and the builder's + /// source is a snapshot, as the log level is already captured in the snapshot. pub fn guest_log_level(mut self, level: LevelFilter) -> Self { self.guest_log_level = Some(level); self @@ -410,9 +441,9 @@ mod tests { #[test] fn build_from_file() { let path = simple_guest_as_string().unwrap(); - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(path) .input_data_size(0x8000) - .build_from_file(path) + .build() .unwrap(); let result = sandbox.call::("Echo", "hello".to_string()).unwrap(); @@ -422,7 +453,7 @@ mod tests { #[test] fn build_from_bytes() { let bytes = std::fs::read(simple_guest_as_string().unwrap()).unwrap(); - let mut sandbox = SandboxBuilder::new().build_from_bytes(bytes).unwrap(); + let mut sandbox = SandboxBuilder::from_bytes(bytes).build().unwrap(); let result = sandbox.call::("Echo", "hello".to_string()).unwrap(); assert_eq!(result, "hello"); @@ -431,10 +462,10 @@ mod tests { #[test] fn build_from_snapshot() { let path = simple_guest_as_string().unwrap(); - let mut sandbox = SandboxBuilder::new().build_from_file(path).unwrap(); + let mut sandbox = SandboxBuilder::from_file(path).build().unwrap(); let snapshot = sandbox.snapshot().unwrap(); - let mut restored = SandboxBuilder::new().build_from_snapshot(snapshot).unwrap(); + let mut restored = SandboxBuilder::from_snapshot(snapshot).build().unwrap(); let result = restored .call::("Echo", "hello".to_string()) @@ -445,20 +476,20 @@ mod tests { #[test] fn build_from_snapshot_errors_on_ignored_settings() { let path = simple_guest_as_string().unwrap(); - let mut sandbox = SandboxBuilder::new().build_from_file(path).unwrap(); + let mut sandbox = SandboxBuilder::from_file(path).build().unwrap(); let snapshot = sandbox.snapshot().unwrap(); assert!( - SandboxBuilder::new() + SandboxBuilder::from_snapshot(snapshot.clone()) .init_data([0u8; 8], MemoryRegionFlags::READ) - .build_from_snapshot(snapshot.clone()) + .build() .is_err() ); assert!( - SandboxBuilder::new() + SandboxBuilder::from_snapshot(snapshot) .guest_log_level(LevelFilter::INFO) - .build_from_snapshot(snapshot) + .build() .is_err() ); } diff --git a/src/hyperlight_host/src/sandbox/host_funcs.rs b/src/hyperlight_host/src/sandbox/host_funcs.rs index c6704c078..a5b9a645d 100644 --- a/src/hyperlight_host/src/sandbox/host_funcs.rs +++ b/src/hyperlight_host/src/sandbox/host_funcs.rs @@ -85,10 +85,10 @@ impl Default for HostFunctions { /// `HostPrint` function (writes UTF-8 strings to the host's /// stdout in green). /// - /// This matches the default registry installed by - /// `SandboxBuilder::new()`, so a snapshot taken from a + /// This matches the default registry installed by the + /// `SandboxBuilder` constructors, so a snapshot taken from a /// regular sandbox can be loaded with - /// `SandboxBuilder::new().build_from_snapshot(snap)` + /// `SandboxBuilder::from_snapshot(snap).build()` /// 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 8613546dc..a6df3b40e 100644 --- a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs +++ b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs @@ -27,7 +27,6 @@ use crate::mem::shared_mem::{HostSharedMemory, SharedMemory as _}; use crate::metrics::{ METRIC_GUEST_ERROR, METRIC_GUEST_ERROR_LABEL_CODE, maybe_time_and_emit_guest_call, }; -use crate::sandbox::builder::SandboxBuilder; use crate::{HyperlightError, Result, log_then_return}; /// The lifecycle state of a [`MultiUseSandbox`]. @@ -107,14 +106,6 @@ pub struct MultiUseSandbox { pub type PtRootFinder = Box Vec + Send>; impl MultiUseSandbox { - /// Start building a sandbox. - /// - /// Returns a [`SandboxBuilder`] with default settings. Adjust it, then call - /// one of its `build_from_*` methods to get a `MultiUseSandbox`. - pub fn builder() -> SandboxBuilder { - SandboxBuilder::new() - } - fn check_ready(&self) -> Result<()> { match self.status { SandboxStatus::Ready => Ok(()), @@ -190,7 +181,7 @@ impl MultiUseSandbox { /// # use hyperlight_host::{HostFunctions, MultiUseSandbox, SandboxBuilder}; /// # fn example() -> Result<(), Box> { /// // Create and initialize a sandbox the normal way - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Capture a snapshot of the initialized state /// let snapshot = sandbox.snapshot()?; @@ -353,7 +344,7 @@ impl MultiUseSandbox { /// ```no_run /// # use hyperlight_host::SandboxBuilder; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Modify sandbox state /// sandbox.call_guest_function_by_name::("SetValue", 42)?; @@ -484,7 +475,7 @@ impl MultiUseSandbox { /// ```no_run /// # use hyperlight_host::SandboxBuilder; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Take initial snapshot from this sandbox /// let snapshot = sandbox.snapshot()?; @@ -507,7 +498,7 @@ impl MultiUseSandbox { /// ```no_run /// # use hyperlight_host::SandboxBuilder; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Take snapshot before potentially poisoning operation /// let snapshot = sandbox.snapshot()?; @@ -637,7 +628,7 @@ impl MultiUseSandbox { /// ```no_run /// # use hyperlight_host::SandboxBuilder; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Call function with no arguments /// let result: i32 = sandbox.call_guest_function_by_name("GetCounter", ())?; @@ -699,7 +690,7 @@ impl MultiUseSandbox { /// ```no_run /// # use hyperlight_host::SandboxBuilder; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Call function with no arguments /// let result: i32 = sandbox.call("GetCounter", ())?; @@ -724,7 +715,7 @@ impl MultiUseSandbox { /// ```no_run /// # use hyperlight_host::SandboxBuilder; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Take snapshot before risky operation /// let snapshot = sandbox.snapshot()?; @@ -972,7 +963,7 @@ impl MultiUseSandbox { /// # use std::thread; /// # use std::time::Duration; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Get interrupt handle before starting long-running operation /// let interrupt_handle = sandbox.interrupt_handle(); @@ -1073,7 +1064,7 @@ impl MultiUseSandbox { /// ```no_run /// # use hyperlight_host::SandboxBuilder; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// if sandbox.status().is_poisoned() { /// println!("Sandbox is poisoned"); @@ -1193,8 +1184,8 @@ mod tests { #[test] fn poison() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sbox.snapshot().unwrap(); @@ -1280,11 +1271,11 @@ mod tests { #[test] fn host_func_error() { let path = simple_guest_as_pathbuf(); - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(path) .host_function("HostError", || -> Result<()> { Err(HyperlightError::Error("hi".to_string())) }) - .build_from_file(path) + .build() .unwrap(); // will exhaust io if leaky @@ -1305,7 +1296,7 @@ mod tests { #[test] fn call_host_func_expect_error() { let path = simple_guest_as_pathbuf(); - let mut sandbox = SandboxBuilder::new().build_from_file(path).unwrap(); + let mut sandbox = SandboxBuilder::from_file(path).build().unwrap(); sandbox .call::<()>("CallHostExpectError", "SomeUnknownHostFunc".to_string()) .unwrap(); @@ -1315,11 +1306,11 @@ mod tests { #[test] fn io_buffer_reset() { let path = simple_guest_as_pathbuf(); - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(path) .input_data_size(4096) .output_data_size(4096) .host_function("HostAdd", |a: i32, b: i32| a + b) - .build_from_file(path) + .build() .unwrap(); // will exhaust io if leaky. Tests both success and error paths @@ -1336,8 +1327,8 @@ mod tests { /// Tests that call_guest_function_by_name restores the state correctly #[test] fn test_call_guest_function_by_name() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sbox.snapshot().unwrap(); @@ -1366,7 +1357,7 @@ mod tests { // total, and then add some more for the eagerly-copied page // tables on amd64 let scratch_size = { - let defaults = SandboxBuilder::new(); + let defaults = SandboxConfiguration::default(); hyperlight_common::layout::min_scratch_size( defaults.get_input_data_size(), defaults.get_output_data_size(), @@ -1374,20 +1365,20 @@ mod tests { } + 0x10000 + 0x10000; - let mut sbox1 = SandboxBuilder::new() + let mut sbox1 = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .heap_size(HEAP_SIZE) .scratch_size(scratch_size) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); for _ in 0..1000 { sbox1.call::("Echo", "hello".to_string()).unwrap(); } - let mut sbox2 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .heap_size(HEAP_SIZE) .scratch_size(scratch_size) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); for i in 0..1000 { @@ -1404,8 +1395,8 @@ mod tests { /// and restoring a snapshot from before evolving restores the previous state #[test] fn snapshot_evolve_restore_handles_state_correctly() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sbox.snapshot().unwrap(); @@ -1422,8 +1413,8 @@ mod tests { #[test] fn test_trigger_exception_on_guest() { - let mut multi_use_sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut multi_use_sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let res: Result<()> = multi_use_sandbox.call("TriggerException", ()); @@ -1455,7 +1446,7 @@ mod tests { for _ in 0..SANDBOXES_PER_THREAD { let guest_path = simple_guest_as_pathbuf(); - let mut sandbox = SandboxBuilder::new().build_from_file(guest_path).unwrap(); + let mut sandbox = SandboxBuilder::from_file(guest_path).build().unwrap(); let result: i32 = sandbox.call("GetStatic", ()).unwrap(); assert_eq!(result, 0); @@ -1489,8 +1480,8 @@ mod tests { #[test] fn test_mmap() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let expected = b"hello world"; @@ -1520,8 +1511,8 @@ mod tests { // Makes sure MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE executable but not writable #[test] fn test_mmap_write_exec() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); #[cfg(target_arch = "x86_64")] @@ -1597,8 +1588,8 @@ mod tests { #[test] fn snapshot_restore_handles_remapping_correctly() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); // 1. Take snapshot 1 with no additional regions mapped @@ -1663,8 +1654,8 @@ mod tests { /// target ever mapping the region. #[test] fn snapshot_restore_across_sandboxes_preserves_mapped_region_contents() { - let mut source = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let map_mem = allocate_guest_memory(); @@ -1686,8 +1677,8 @@ mod tests { let snapshot = source.snapshot().unwrap(); - let mut target = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); assert_eq!(target.vm.get_mapped_regions().count(), 0); @@ -1711,12 +1702,12 @@ mod tests { #[test] fn snapshot_restore_across_sandboxes() { - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); - let mut sandbox2 = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox2 = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); sandbox.call::("AddToStatic", 42i32).unwrap(); @@ -2022,14 +2013,14 @@ mod tests { #[test] fn snapshot_restore_rejects_incompatible_layout() { - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .heap_size(0x10_000) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); - let mut sandbox2 = SandboxBuilder::new() + let mut sandbox2 = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .heap_size(0x20_000) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sandbox.snapshot().unwrap(); @@ -2041,14 +2032,14 @@ mod tests { /// rejected `restore` leaves the target usable. #[test] fn snapshot_restore_failure_leaves_target_usable() { - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .heap_size(0x10_000) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); - let mut target = SandboxBuilder::new() + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .heap_size(0x20_000) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); target.call::("AddToStatic", 5i32).unwrap(); @@ -2071,14 +2062,14 @@ mod tests { /// unmaps anything the target had mapped. #[test] fn snapshot_restore_across_sandboxes_target_has_mapped_regions() { - let mut source = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source.call::("AddToStatic", 23i32).unwrap(); let snapshot = source.snapshot().unwrap(); - let mut target = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let map_mem = allocate_guest_memory(); let guest_base = 0x200000000_usize; @@ -2096,8 +2087,8 @@ mod tests { /// GVA. #[test] fn snapshot_restore_across_sandboxes_both_have_different_mapped_regions() { - let mut source = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let source_mem = allocate_guest_memory(); let source_base = 0x200000000_usize; @@ -2116,8 +2107,8 @@ mod tests { source.call::("AddToStatic", 9i32).unwrap(); let snapshot = source.snapshot().unwrap(); - let mut target = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let target_mem = allocate_guest_memory(); let target_base = 0x300000000_usize; @@ -2146,14 +2137,14 @@ mod tests { /// Repeated restore of the same snapshot is idempotent. #[test] fn snapshot_restore_across_sandboxes_repeated() { - let mut source = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source.call::("AddToStatic", 7i32).unwrap(); let snapshot = source.snapshot().unwrap(); - let mut target = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); target.restore(snapshot.clone()).unwrap(); @@ -2170,8 +2161,8 @@ mod tests { /// that restore() calls reset_vcpu(). #[test] fn snapshot_restore_resets_debug_registers() { - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sandbox.snapshot().unwrap(); @@ -2244,8 +2235,8 @@ mod tests { /// leak into the next call. #[test] fn stale_abort_buffer_does_not_leak_across_calls() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); // Simulate a partial abort @@ -2276,10 +2267,10 @@ mod tests { for (name, heap_size) in test_cases { let path = simple_guest_as_pathbuf(); - let sbox = SandboxBuilder::new() + let sbox = SandboxBuilder::from_file(path) .heap_size(heap_size) .scratch_size(0x100000) - .build_from_file(path) + .build() .unwrap_or_else(|e| panic!("Failed to create {} sandbox: {}", name, e)); drop(sbox); @@ -2290,7 +2281,7 @@ mod tests { #[cfg(feature = "trace_guest")] fn sandbox_for_gva_tests() -> MultiUseSandbox { let path = simple_guest_as_pathbuf(); - SandboxBuilder::new().build_from_file(path).unwrap() + SandboxBuilder::from_file(path).build().unwrap() } /// Helper: read memory at `gva` of length `len` from the guest side via @@ -2404,8 +2395,8 @@ mod tests { let (path, expected_bytes) = create_test_file("hyperlight_test_map_file_cow_basic.bin", expected); - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let guest_base: u64 = 0x1_0000_0000; @@ -2440,8 +2431,8 @@ mod tests { let content = &[0xBB; 4096]; let (path, _) = create_test_file("hyperlight_test_map_file_cow_readonly.bin", content); - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let guest_base: u64 = 0x1_0000_0000; @@ -2470,8 +2461,8 @@ mod tests { fn test_map_file_cow_poisoned() { let (path, _) = create_test_file("hyperlight_test_map_file_cow_poison.bin", &[0xCC; 4096]); - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sbox.snapshot().unwrap(); @@ -2504,12 +2495,12 @@ mod tests { let guest_base: u64 = 0x1_0000_0000; - let mut sbox1 = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox1 = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); - let mut sbox2 = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox2 = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); // Map the same file into both sandboxes @@ -2564,8 +2555,8 @@ mod tests { handles.push(thread::spawn(move || { barrier.wait(); - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let guest_base: u64 = 0x1_0000_0000; @@ -2597,8 +2588,8 @@ mod tests { let (path, _) = create_test_file("hyperlight_test_map_file_cow_cleanup.bin", &[0xDD; 4096]); { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); sbox.map_file_cow(&path, 0x1_0000_0000).unwrap(); @@ -2618,8 +2609,8 @@ mod tests { let (path, expected_bytes) = create_test_file("hyperlight_test_map_file_cow_snapshot_remap.bin", expected); - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let guest_base: u64 = 0x1_0000_0000; @@ -2680,8 +2671,8 @@ mod tests { let (path, expected_bytes) = create_test_file("hyperlight_test_map_file_cow_snap_restore.bin", expected); - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let guest_base: u64 = 0x1_0000_0000; @@ -2886,8 +2877,8 @@ mod tests { #[test] fn map_region_rejects_overlapping_regions() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let mem1 = allocate_guest_memory(); @@ -2909,8 +2900,8 @@ mod tests { #[test] fn map_region_rejects_partial_overlap() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); // Use multi-page regions so partial overlap is geometrically possible @@ -2934,8 +2925,8 @@ mod tests { #[test] fn map_region_allows_adjacent_non_overlapping() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let mem1 = allocate_guest_memory(); @@ -2954,8 +2945,8 @@ mod tests { #[test] fn map_region_rejects_overlap_with_snapshot() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); // Try to map at BASE_ADDRESS (0x1000) which overlaps the snapshot region @@ -2974,8 +2965,8 @@ mod tests { #[test] fn map_region_rejects_overlap_with_scratch() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); // The scratch region occupies the top of the GPA space @@ -3037,8 +3028,8 @@ mod tests { #[test] fn kernel_gs_base_does_not_leak_through_swapgs() { - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let original: u64 = sandbox.call("ReadKernelGsBaseViaSwapgs", ()).unwrap(); @@ -3071,10 +3062,10 @@ mod tests { #[test] fn snapshot_msr_values_survive_full_in_memory_lifecycle() { - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[KERNEL_GS_BASE]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let first = 0x1111; let second = 0x2222; @@ -3102,10 +3093,10 @@ mod tests { first ); - let mut clone = SandboxBuilder::new() + let mut clone = SandboxBuilder::from_snapshot(first_snapshot.clone()) .guest_msrs(&[KERNEL_GS_BASE]) .unwrap() - .build_from_snapshot(first_snapshot.clone()) + .build() .unwrap(); assert_eq!(clone.call::("ReadMSR", KERNEL_GS_BASE).unwrap(), first); @@ -3120,10 +3111,10 @@ mod tests { third ); - let mut second_clone = SandboxBuilder::new() + let mut second_clone = SandboxBuilder::from_snapshot(third_snapshot) .guest_msrs(&[KERNEL_GS_BASE]) .unwrap() - .build_from_snapshot(third_snapshot) + .build() .unwrap(); assert_eq!( second_clone.call::("ReadMSR", KERNEL_GS_BASE).unwrap(), @@ -3140,10 +3131,10 @@ mod tests { fn equivalent_msr_configs_are_order_independent_across_sandboxes() { let source_order = [KERNEL_GS_BASE, SYSENTER_CS]; let target_order = [SYSENTER_CS, KERNEL_GS_BASE]; - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&source_order) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source .call::<()>("WriteMSR", (KERNEL_GS_BASE, 0x4444u64)) @@ -3158,10 +3149,10 @@ mod tests { assert_eq!(source.call::("ReadMSR", SYSENTER_CS).unwrap(), 0x5555); let snapshot = source.snapshot().unwrap(); - let mut target = SandboxBuilder::new() + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&target_order) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); target .call::<()>("WriteMSR", (KERNEL_GS_BASE, 0xAAAAu64)) @@ -3181,10 +3172,10 @@ mod tests { ); assert_eq!(target.call::("ReadMSR", SYSENTER_CS).unwrap(), 0x5555); - let mut clone = SandboxBuilder::new() + let mut clone = SandboxBuilder::from_snapshot(snapshot) .guest_msrs(&target_order) .unwrap() - .build_from_snapshot(snapshot) + .build() .unwrap(); assert_eq!( clone.call::("ReadMSR", KERNEL_GS_BASE).unwrap(), @@ -3200,28 +3191,28 @@ mod tests { fn snapshot_restores_into_superset_guest_msrs() { const SYSENTER_ESP: u32 = 0x175; let sentinel: u64 = 0x1234; - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[SYSENTER_CS]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source .call::<()>("WriteMSR", (SYSENTER_CS, sentinel)) .unwrap(); let snapshot = source.snapshot().unwrap(); - let mut clone = SandboxBuilder::new() + let mut clone = SandboxBuilder::from_snapshot(snapshot.clone()) .guest_msrs(&[SYSENTER_CS, SYSENTER_ESP]) .unwrap() - .build_from_snapshot(snapshot.clone()) + .build() .unwrap(); assert_eq!(clone.call::("ReadMSR", SYSENTER_CS).unwrap(), sentinel); let baseline: u64 = clone.call("ReadMSR", SYSENTER_ESP).unwrap(); - let mut target = SandboxBuilder::new() + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[SYSENTER_CS, SYSENTER_ESP]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); target .call::<()>("WriteMSR", (SYSENTER_ESP, baseline ^ 0x55)) @@ -3245,10 +3236,10 @@ mod tests { #[test] fn snapshot_rejects_non_superset_guest_msrs() { const SYSENTER_ESP: u32 = 0x175; - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[SYSENTER_CS]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source .call::<()>("WriteMSR", (SYSENTER_CS, 0x1234u64)) @@ -3259,17 +3250,17 @@ mod tests { // disjoint MSR, both reject because the snapshot's SYSENTER_CS is // neither declared by the destination nor a core MSR. for dest in [&[][..], &[SYSENTER_ESP][..]] { - let err = SandboxBuilder::new() + let err = SandboxBuilder::from_snapshot(snapshot.clone()) .guest_msrs(dest) .unwrap() - .build_from_snapshot(snapshot.clone()) + .build() .expect_err("from_snapshot must reject an unrestorable snapshot MSR"); assert_snapshot_msr_index_invalid(&err); - let mut target = SandboxBuilder::new() + let mut target = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(dest) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let err = target .restore(snapshot.clone()) @@ -3293,10 +3284,10 @@ mod tests { ); assert!(snapshot.msrs().is_none()); - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_snapshot(snapshot.clone()) .guest_msrs(&[KERNEL_GS_BASE]) .unwrap() - .build_from_snapshot(snapshot.clone()) + .build() .unwrap(); let baseline: u64 = sandbox.call("ReadMSR", KERNEL_GS_BASE).unwrap(); sandbox @@ -3322,8 +3313,8 @@ mod tests { const MSR_X2APIC_BASE: u32 = 0x800; const APIC_BASE_DEFAULT: u64 = 0xFEE0_0900; - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sandbox.snapshot().unwrap(); @@ -3357,8 +3348,8 @@ mod tests { } } - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sbox.snapshot().unwrap(); @@ -3388,8 +3379,8 @@ mod tests { #[test] #[cfg(target_arch = "x86_64")] fn nested_virtualization_is_hidden_from_guest() { - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let features: u32 = sandbox.call("NestedVirtualizationCpuid", ()).unwrap(); @@ -3405,8 +3396,8 @@ mod tests { return; } - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sandbox.snapshot().unwrap(); @@ -3433,8 +3424,8 @@ mod tests { #[test] #[cfg(target_arch = "x86_64")] fn guest_cannot_enter_vmx_operation() { - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let result = sandbox.call::<()>("EnableVmxOperation", ()); @@ -3452,8 +3443,8 @@ mod tests { #[test] #[cfg(target_arch = "x86_64")] fn guest_vmlaunch_faults() { - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let result = sandbox.call::<()>("ExecuteVmlaunch", ()); @@ -3475,8 +3466,8 @@ mod tests { return; } - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); assert!( @@ -3490,10 +3481,10 @@ mod tests { #[cfg(target_arch = "x86_64")] fn test_allow_non_resettable_msr_fails_creation() { // IA32_PRED_CMD, a write-only command MSR - let err = SandboxBuilder::new() + let err = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[0x49]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap_err(); assert_msr_not_declarable(&err, 0x49); @@ -3510,10 +3501,10 @@ mod tests { } // IA32_MISC_ENABLE: host-probeable, not in MSR_TABLE - let err = SandboxBuilder::new() + let err = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[0x1A0]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .expect_err("an unclassified declared MSR must be rejected at creation"); assert_msr_not_declarable(&err, 0x1A0); @@ -3525,10 +3516,10 @@ mod tests { // Resettable MSRs the guest may write once declared. let msrs: [u32; 4] = [0x174, 0x175, 0x176, 0xC000_0102]; - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&msrs) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let baseline_snapshot = sbox.snapshot().unwrap(); @@ -3557,10 +3548,10 @@ mod tests { let msr_index: u32 = 0xC000_0102; // IA32_KERNEL_GS_BASE let sentinel: u64 = 0xCAFE_F00D; - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[msr_index]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let baseline = sbox.snapshot().unwrap(); @@ -3594,8 +3585,8 @@ mod tests { } for msr_index in [0x1D9_u32, 0x800] { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let result = sbox.call::<()>("WriteMSR", (msr_index, 0x1u64)); @@ -3622,8 +3613,8 @@ mod tests { const KVM_CUSTOM_MSR_START: u32 = 0x4B56_4D00; const KVM_CUSTOM_MSR_END: u32 = 0x4B56_4DFF; - let mut sandbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sandbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snapshot = sandbox.snapshot().unwrap(); @@ -3661,8 +3652,8 @@ mod tests { (0xC001_0117, "AMD VM_HSAVE_PA"), ]; - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); for &(msr, _name) in cases { @@ -3675,8 +3666,8 @@ mod tests { #[test] #[cfg(target_arch = "x86_64")] fn misc_enable_guest_write_does_not_survive_restore() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); assert_msr_write_does_not_survive_restore(&mut sbox, 0x1A0, 1u64 << 40); } @@ -3695,8 +3686,8 @@ mod tests { #[cfg(not(kvm))] let is_kvm = false; - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let reset_indices: Vec = sbox.vm.reset_set_indices(); @@ -3926,8 +3917,8 @@ mod tests { return; } - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let baseline = sbox.snapshot().unwrap(); @@ -4072,8 +4063,8 @@ mod tests { #[test] #[cfg(all(any(mshv3, target_os = "windows"), target_arch = "x86_64"))] fn active_ssp_does_not_leak_across_restore() { - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); if !sbox.call::("CetShadowStackSupported", ()).unwrap() { @@ -4110,8 +4101,8 @@ mod tests { return; } - let mut sbox = SandboxBuilder::new() - .build_from_file(simple_guest_as_pathbuf()) + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); assert!( !sbox.call::("CetShadowStackSupported", ()).unwrap(), @@ -4120,10 +4111,10 @@ mod tests { // With CET hidden the host cannot read or write IA32_S_CET, so // allowing it is rejected at VM creation. - let err = SandboxBuilder::new() + let err = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[MSR_S_CET]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .expect_err("allowing IA32_S_CET must be rejected when CET is hidden"); assert_msr_not_declarable(&err, MSR_S_CET); } @@ -4142,15 +4133,15 @@ mod tests { fn make_sandbox() -> MultiUseSandbox { let path = simple_guest_as_pathbuf(); - SandboxBuilder::new().build_from_file(path).unwrap() + SandboxBuilder::from_file(path).build().unwrap() } /// Sandbox with an extra `Add(i32, i32) -> i32` host function. fn make_sandbox_with_add() -> MultiUseSandbox { let path = simple_guest_as_pathbuf(); - SandboxBuilder::new() + SandboxBuilder::from_file(path) .host_function("Add", |a: i32, b: i32| a + b) - .build_from_file(path) + .build() .unwrap() } @@ -4166,7 +4157,7 @@ mod tests { let mut sbox = make_sandbox(); sbox.call::("AddToStatic", 11i32).unwrap(); let snapshot = sbox.snapshot().unwrap(); - let mut sbox2 = SandboxBuilder::new().build_from_snapshot(snapshot).unwrap(); + let mut sbox2 = SandboxBuilder::from_snapshot(snapshot).build().unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 11); let echoed: String = sbox2.call("Echo", "hi".to_string()).unwrap(); assert_eq!(echoed, "hi"); @@ -4178,8 +4169,8 @@ mod tests { let snap = Snapshot::from_env(GuestBinary::FilePath(path), SandboxConfiguration::default()) .unwrap(); - let mut sbox = SandboxBuilder::new() - .build_from_snapshot(Arc::new(snap)) + let mut sbox = SandboxBuilder::from_snapshot(Arc::new(snap)) + .build() .unwrap(); assert_eq!(sbox.call::("GetStatic", ()).unwrap(), 0); } @@ -4193,11 +4184,11 @@ mod tests { sbox.call::("AddToStatic", 3i32).unwrap(); let snapshot = sbox.snapshot().unwrap(); - let mut a = SandboxBuilder::new() - .build_from_snapshot(snapshot.clone()) + let mut a = SandboxBuilder::from_snapshot(snapshot.clone()) + .build() .unwrap(); - let mut b = SandboxBuilder::new() - .build_from_snapshot(snapshot.clone()) + let mut b = SandboxBuilder::from_snapshot(snapshot.clone()) + .build() .unwrap(); assert_eq!(a.call::("GetStatic", ()).unwrap(), 3); assert_eq!(b.call::("GetStatic", ()).unwrap(), 3); @@ -4217,9 +4208,9 @@ mod tests { let mut sbox = make_sandbox_with_add(); sbox.call::("AddToStatic", 5i32).unwrap(); let snap = sbox.snapshot().unwrap(); - let mut sbox2 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(snap) .host_functions(host_funcs_with_matching_add()) - .build_from_snapshot(snap) + .build() .unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 5); } @@ -4228,8 +4219,8 @@ mod tests { fn rejects_missing_host_function() { let mut sbox = make_sandbox_with_add(); let snap = sbox.snapshot().unwrap(); - let err = SandboxBuilder::new() - .build_from_snapshot(snap) + let err = SandboxBuilder::from_snapshot(snap) + .build() .expect_err("missing `Add` must be rejected"); assert!( matches!( @@ -4273,9 +4264,9 @@ mod tests { let mut sbox_with_add = make_sandbox_with_add(); let snap = sbox_with_add.snapshot().unwrap(); let path = simple_guest_as_pathbuf(); - let mut sbox_wrong_add = SandboxBuilder::new() + let mut sbox_wrong_add = SandboxBuilder::from_file(path) .host_function("Add", |a: String, b: String| format!("{a}{b}")) - .build_from_file(path) + .build() .unwrap(); let err = sbox_wrong_add .restore(snap) @@ -4300,10 +4291,10 @@ mod tests { let snap = source.snapshot().unwrap(); let path = simple_guest_as_pathbuf(); - let mut target = SandboxBuilder::new() + let mut target = SandboxBuilder::from_file(path) .host_function("Add", |a: i32, b: i32| a + b) .host_function("Mul", |a: i32, b: i32| a * b) - .build_from_file(path) + .build() .unwrap(); target.restore(snap).unwrap(); @@ -4317,9 +4308,9 @@ mod tests { let mut hf = HostFunctions::default(); hf.register_host_function("Add", |a: String, b: String| Ok(format!("{a}{b}"))) .unwrap(); - let err = SandboxBuilder::new() + let err = SandboxBuilder::from_snapshot(snap) .host_functions(hf) - .build_from_snapshot(snap) + .build() .expect_err("signature mismatch on `Add` must be rejected"); assert!( matches!( @@ -4342,9 +4333,9 @@ 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 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(snap) .host_functions(hf) - .build_from_snapshot(snap) + .build() .unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 9); } @@ -4357,7 +4348,7 @@ mod tests { sbox.call::("AddToStatic", 4i32).unwrap(); let snap1 = sbox.snapshot().unwrap(); - let mut sbox2 = SandboxBuilder::new().build_from_snapshot(snap1).unwrap(); + let mut sbox2 = SandboxBuilder::from_snapshot(snap1).build().unwrap(); sbox2.call::("AddToStatic", 6i32).unwrap(); let snap2 = sbox2.snapshot().unwrap(); @@ -4367,7 +4358,7 @@ mod tests { sbox2.restore(snap2.clone()).unwrap(); assert_eq!(sbox2.call::("GetStatic", ()).unwrap(), 10); - let mut sbox3 = SandboxBuilder::new().build_from_snapshot(snap2).unwrap(); + let mut sbox3 = SandboxBuilder::from_snapshot(snap2).build().unwrap(); assert_eq!(sbox3.call::("GetStatic", ()).unwrap(), 10); } @@ -4376,17 +4367,17 @@ mod tests { #[test] fn supplied_host_function_is_callable() { let path = simple_guest_as_pathbuf(); - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_file(path) .host_function("Echo42", || 1i64) - .build_from_file(path) + .build() .unwrap(); let snap = sbox.snapshot().unwrap(); let mut hf = HostFunctions::default(); hf.register_host_function("Echo42", || Ok(42i64)).unwrap(); - let mut sbox2 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(snap) .host_functions(hf) - .build_from_snapshot(snap) + .build() .unwrap(); let got: i64 = sbox2 @@ -4409,9 +4400,9 @@ mod tests { let mut hf = HostFunctions::default(); hf.register_host_function("Unrelated", |a: i32| Ok(a + 1)) .unwrap(); - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_snapshot(Arc::new(snap)) .host_functions(hf) - .build_from_snapshot(Arc::new(snap)) + .build() .unwrap(); assert_eq!(sbox.call::("GetStatic", ()).unwrap(), 0); } @@ -4430,7 +4421,7 @@ mod tests { let gen2 = snap2.snapshot_generation(); assert_eq!(gen2, gen1 + 1); - let mut sbox2 = SandboxBuilder::new().build_from_snapshot(snap2).unwrap(); + let mut sbox2 = SandboxBuilder::from_snapshot(snap2).build().unwrap(); sbox2.call::("AddToStatic", 1i32).unwrap(); let snap3 = sbox2.snapshot().unwrap(); assert_eq!(snap3.snapshot_generation(), gen2 + 1); @@ -4452,8 +4443,8 @@ mod tests { // host function, so building a sandbox from it without // `Echo42` must fail. let snap = sbox.snapshot().unwrap(); - let err = SandboxBuilder::new() - .build_from_snapshot(snap) + let err = SandboxBuilder::from_snapshot(snap) + .build() .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 678d1d162..0331628de 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file/mod.rs @@ -327,7 +327,7 @@ impl Snapshot { /// # use hyperlight_host::SandboxBuilder; /// # use hyperlight_host::sandbox::snapshot::OciTag; /// # fn example() -> Result<(), Box> { - /// let mut sandbox = SandboxBuilder::new().build_from_file("guest.bin")?; + /// let mut sandbox = SandboxBuilder::from_file("guest.bin").build()?; /// /// // Capture the initialized state and write it to an OCI layout on disk. /// let snapshot = sandbox.snapshot()?; diff --git a/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs b/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs index 7e200a50e..10fae71ec 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/file_tests.rs @@ -19,12 +19,12 @@ use crate::{GuestBinary, HostFunctions, MultiUseSandbox, SandboxBuilder}; fn create_test_sandbox() -> MultiUseSandbox { let path = simple_guest_as_pathbuf(); - SandboxBuilder::new().build_from_file(path).unwrap() + SandboxBuilder::from_file(path).build().unwrap() } fn create_c_test_sandbox() -> MultiUseSandbox { let path = c_simple_guest_as_pathbuf(); - SandboxBuilder::new().build_from_file(path).unwrap() + SandboxBuilder::from_file(path).build().unwrap() } fn random_sequence(sandbox: &mut MultiUseSandbox) -> [i32; 4] { @@ -267,10 +267,10 @@ fn disk_snapshot_restores_declared_msr_value() { const SYSENTER_CS: u32 = 0x174; let sentinel: u64 = 0xDEAD_BEEF; - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[SYSENTER_CS]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source .call::<()>("WriteMSR", (SYSENTER_CS, sentinel)) @@ -282,10 +282,10 @@ fn disk_snapshot_restores_declared_msr_value() { snap.save(&path, &OciTag::new("latest").unwrap()).unwrap(); let loaded = Arc::new(Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap()); - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_snapshot(loaded.clone()) .guest_msrs(&[SYSENTER_CS]) .unwrap() - .build_from_snapshot(loaded.clone()) + .build() .unwrap(); assert_eq!(sbox.call::("ReadMSR", SYSENTER_CS).unwrap(), sentinel); @@ -304,10 +304,10 @@ fn disk_snapshot_restores_into_superset_guest_msrs() { const SYSENTER_ESP: u32 = 0x175; let sentinel: u64 = 0xDEAD_BEEF; - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[SYSENTER_CS]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source .call::<()>("WriteMSR", (SYSENTER_CS, sentinel)) @@ -319,10 +319,10 @@ fn disk_snapshot_restores_into_superset_guest_msrs() { snap.save(&path, &OciTag::new("latest").unwrap()).unwrap(); let loaded = Arc::new(Snapshot::checked_load(&path, OciTag::new("latest").unwrap()).unwrap()); - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_snapshot(loaded) .guest_msrs(&[SYSENTER_CS, SYSENTER_ESP]) .unwrap() - .build_from_snapshot(loaded) + .build() .unwrap(); assert_eq!(sbox.call::("ReadMSR", SYSENTER_CS).unwrap(), sentinel); } @@ -337,10 +337,10 @@ fn disk_snapshot_non_superset_guest_msrs_rejected() { const SYSENTER_CS: u32 = 0x174; let sentinel: u64 = 0x1234; - let mut source = SandboxBuilder::new() + let mut source = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .guest_msrs(&[SYSENTER_CS]) .unwrap() - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); source .call::<()>("WriteMSR", (SYSENTER_CS, sentinel)) @@ -728,9 +728,9 @@ fn call_snapshot_without_sregs_rejected() { /// custom `Add(i32, i32) -> i32`. fn create_sandbox_with_custom_host_funcs() -> MultiUseSandbox { let path = simple_guest_as_pathbuf(); - SandboxBuilder::new() + SandboxBuilder::from_file(path) .host_function("Add", |a: i32, b: i32| Ok(a + b)) - .build_from_file(path) + .build() .unwrap() } @@ -825,9 +825,9 @@ fn from_snapshot_accepts_extra_host_functions() { #[test] fn from_snapshot_accepts_zero_arg_host_function() { let path = simple_guest_as_pathbuf(); - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_file(path) .host_function("Zero", || Ok(7i64)) - .build_from_file(path) + .build() .unwrap(); let snap = sbox.snapshot().unwrap(); @@ -2570,14 +2570,14 @@ fn index_json_too_large_on_write_rejected() { #[test] fn config_blob_too_large_on_write_rejected() { let guest = simple_guest_as_pathbuf(); - let mut builder = SandboxBuilder::new(); + let mut builder = SandboxBuilder::from_file(guest); // Each host function adds its name and signature to the config // JSON. Long names reach the 1 MiB cap with a modest count. let long = "h".repeat(300); for i in 0..3000 { builder = builder.host_function(format!("{long}{i}"), |a: i32, b: i32| Ok(a + b)); } - let mut sbox = builder.build_from_file(guest).unwrap(); + let mut sbox = builder.build().unwrap(); let snap = sbox.snapshot().unwrap(); let dir = tempfile::tempdir().unwrap(); @@ -2768,9 +2768,9 @@ fn round_trip_preserves_stack_top_gva() { #[test] fn round_trip_preserves_non_default_scratch_size() { let custom_scratch: usize = 256 * 1024; - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .scratch_size(custom_scratch) - .build_from_file(simple_guest_as_pathbuf()) + .build() .unwrap(); let snap = sbox.snapshot().unwrap(); let original = snap.layout().get_scratch_size(); @@ -3159,12 +3159,12 @@ fn from_snapshot_silently_ignores_layout_overrides() { let original_heap = snapshot.layout().heap_size(); let original_scratch = snapshot.layout().get_scratch_size(); - let mut sbox2 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(snapshot.clone()) .input_data_size(original_input * 2) .output_data_size(original_output * 2) .heap_size((original_heap as u64) * 2) .scratch_size(original_scratch * 2) - .build_from_snapshot(snapshot.clone()) + .build() .unwrap(); sbox2.call::("GetStatic", ()).unwrap(); @@ -3277,9 +3277,9 @@ fn from_snapshot_honors_guest_core_dump_enabled() { let mut sbox = create_test_sandbox(); let snapshot = sbox.snapshot().unwrap(); - let mut sbox2 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(snapshot) .guest_core_dump(true) - .build_from_snapshot(snapshot) + .build() .unwrap(); let dir = tempfile::tempdir().unwrap(); @@ -3303,9 +3303,9 @@ fn from_snapshot_honors_guest_core_dump_disabled() { let mut sbox = create_test_sandbox(); let snapshot = sbox.snapshot().unwrap(); - let mut sbox2 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(snapshot) .guest_core_dump(false) - .build_from_snapshot(snapshot) + .build() .unwrap(); let dir = tempfile::tempdir().unwrap(); @@ -3333,9 +3333,9 @@ fn round_trip_preserves_non_default_init_data_permissions() { let path = simple_guest_as_pathbuf(); let data: &[u8] = b"perm-pinned-init-data"; - let mut sbox = SandboxBuilder::new() + let mut sbox = SandboxBuilder::from_file(path) .init_data(data, MemoryRegionFlags::READ | MemoryRegionFlags::WRITE) - .build_from_file(path) + .build() .unwrap(); let snap = sbox.snapshot().unwrap(); let expected = snap.layout().init_data_permissions(); diff --git a/src/hyperlight_host/src/sandbox/snapshot/mod.rs b/src/hyperlight_host/src/sandbox/snapshot/mod.rs index a4def5b7a..2efa65d88 100644 --- a/src/hyperlight_host/src/sandbox/snapshot/mod.rs +++ b/src/hyperlight_host/src/sandbox/snapshot/mod.rs @@ -286,8 +286,8 @@ fn map_specials(pt_buf: &GuestPageTableBuffer, scratch_size: usize) { impl Snapshot { /// Create a new snapshot from the guest binary identified by `env`. With the configuration /// specified in `cfg`. - pub(crate) fn from_env<'a, 'b>( - env: impl Into>, + pub(crate) fn from_env<'b>( + env: impl Into>, cfg: SandboxConfiguration, ) -> Result { let env = env.into(); diff --git a/src/hyperlight_host/src/sandbox/uninitialized.rs b/src/hyperlight_host/src/sandbox/uninitialized.rs index 066c80c19..c1bfd1088 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized.rs @@ -80,13 +80,13 @@ impl Debug for UninitializedSandbox { /// A `GuestBinary` is either a buffer or the file path to some data (e.g., a guest binary). #[derive(Debug)] -pub enum GuestBinary<'a> { +pub enum GuestBinary { /// A buffer containing the GuestBinary - Buffer(&'a [u8]), + Buffer(Vec), /// A path to the GuestBinary FilePath(PathBuf), } -impl<'a> GuestBinary<'a> { +impl GuestBinary { /// If the guest binary is identified by a file, canonicalise the path /// /// For [`GuestBinary::FilePath`], this resolves the path to its canonical @@ -127,17 +127,19 @@ impl<'a> From<&'a [u8]> for GuestBlob<'a> { /// /// This struct combines a guest binary (either from a file or memory buffer) with /// optional data that will be available to the guest during execution. +/// +/// The guest binary is owned. `'b` is the lifetime of the borrowed init data. #[derive(Debug)] -pub struct GuestEnvironment<'a, 'b> { +pub struct GuestEnvironment<'b> { /// The guest binary, which can be a file path or a buffer. - pub guest_binary: GuestBinary<'a>, + pub guest_binary: GuestBinary, /// An optional guest blob, which can be used to provide additional data to the guest. pub init_data: Option>, } -impl<'a, 'b> GuestEnvironment<'a, 'b> { +impl<'b> GuestEnvironment<'b> { /// Creates a new `GuestEnvironment` with the given guest binary and an optional guest blob. - pub fn new(guest_binary: GuestBinary<'a>, init_data: Option<&'b [u8]>) -> Self { + pub fn new(guest_binary: GuestBinary, init_data: Option<&'b [u8]>) -> Self { GuestEnvironment { guest_binary, init_data: init_data.map(GuestBlob::from), @@ -145,8 +147,8 @@ impl<'a, 'b> GuestEnvironment<'a, 'b> { } } -impl<'a> From> for GuestEnvironment<'a, '_> { - fn from(guest_binary: GuestBinary<'a>) -> Self { +impl From for GuestEnvironment<'_> { + fn from(guest_binary: GuestBinary) -> Self { GuestEnvironment { guest_binary, init_data: None, @@ -230,8 +232,8 @@ impl UninitializedSandbox { skip(env), parent = Span::current() )] - pub fn new<'a, 'b>( - env: impl Into>, + pub fn new<'b>( + env: impl Into>, cfg: Option, ) -> Result { let cfg = cfg.unwrap_or_default(); @@ -479,7 +481,7 @@ mod tests { let binary_path = simple_guest_as_pathbuf(); let sandbox = - UninitializedSandbox::new(GuestBinary::Buffer(&fs::read(binary_path).unwrap()), None); + UninitializedSandbox::new(GuestBinary::Buffer(fs::read(binary_path).unwrap()), None); assert!(sandbox.is_ok()); // Test with a invalid guest binary buffer @@ -487,7 +489,7 @@ mod tests { let binary_path = simple_guest_as_pathbuf(); let mut bytes = fs::read(binary_path).unwrap(); let _ = bytes.split_off(100); - let sandbox = UninitializedSandbox::new(GuestBinary::Buffer(&bytes), None); + let sandbox = UninitializedSandbox::new(GuestBinary::Buffer(bytes), None); assert!(sandbox.is_err()); } @@ -1275,7 +1277,7 @@ mod tests { let binary_bytes = fs::read(&binary_path).expect("Failed to read binary file"); let snapshot = Arc::new( - Snapshot::from_env(GuestBinary::Buffer(&binary_bytes), Default::default()) + Snapshot::from_env(GuestBinary::Buffer(binary_bytes), Default::default()) .expect("Failed to create snapshot from buffer"), ); diff --git a/src/hyperlight_host/tests/common/mod.rs b/src/hyperlight_host/tests/common/mod.rs index 141e1adb1..3a96a94a9 100644 --- a/src/hyperlight_host/tests/common/mod.rs +++ b/src/hyperlight_host/tests/common/mod.rs @@ -20,14 +20,19 @@ fn c_guest_path() -> PathBuf { // Rust guest helpers // ============================================================================= -/// Builds a Rust guest MultiUseSandbox from `builder`. -pub fn build_rust_sandbox(builder: SandboxBuilder) -> MultiUseSandbox { - builder.build_from_file(rust_guest_path()).unwrap() +/// Builds a Rust guest MultiUseSandbox, applying `configure` to the builder. +pub fn build_rust_sandbox(configure: C) -> MultiUseSandbox +where + C: FnOnce(SandboxBuilder) -> SandboxBuilder, +{ + configure(SandboxBuilder::from_file(rust_guest_path())) + .build() + .unwrap() } /// Creates a new Rust guest MultiUseSandbox. pub fn new_rust_sandbox() -> MultiUseSandbox { - build_rust_sandbox(SandboxBuilder::new()) + build_rust_sandbox(|builder| builder) } /// Runs a test with a Rust guest MultiUseSandbox. @@ -38,21 +43,27 @@ where f(new_rust_sandbox()); } -/// Runs a test with a Rust guest MultiUseSandbox built from `builder`. -pub fn with_rust_sandbox_from(builder: SandboxBuilder, f: F) +/// Runs a test with a Rust guest MultiUseSandbox built with `configure`. +pub fn with_rust_sandbox_from(configure: C, f: F) where + C: FnOnce(SandboxBuilder) -> SandboxBuilder, F: FnOnce(MultiUseSandbox), { - f(build_rust_sandbox(builder)); + f(build_rust_sandbox(configure)); } // ============================================================================= // C guest helpers // ============================================================================= -/// Builds a C guest MultiUseSandbox from `builder`. -pub fn build_c_sandbox(builder: SandboxBuilder) -> MultiUseSandbox { - builder.build_from_file(c_guest_path()).unwrap() +/// Builds a C guest MultiUseSandbox, applying `configure` to the builder. +pub fn build_c_sandbox(configure: C) -> MultiUseSandbox +where + C: FnOnce(SandboxBuilder) -> SandboxBuilder, +{ + configure(SandboxBuilder::from_file(c_guest_path())) + .build() + .unwrap() } /// Runs a test with a C guest MultiUseSandbox. @@ -60,15 +71,16 @@ pub fn with_c_sandbox(f: F) where F: FnOnce(MultiUseSandbox), { - f(build_c_sandbox(SandboxBuilder::new())); + f(build_c_sandbox(|builder| builder)); } -/// Runs a test with a C guest MultiUseSandbox built from `builder`. -pub fn with_c_sandbox_from(builder: SandboxBuilder, f: F) +/// Runs a test with a C guest MultiUseSandbox built with `configure`. +pub fn with_c_sandbox_from(configure: C, f: F) where + C: FnOnce(SandboxBuilder) -> SandboxBuilder, F: FnOnce(MultiUseSandbox), { - f(build_c_sandbox(builder)); + f(build_c_sandbox(configure)); } // ============================================================================= @@ -94,6 +106,6 @@ where F: Fn(MultiUseSandbox), { with_all_guests(|path| { - f(SandboxBuilder::new().build_from_file(path).unwrap()); + f(SandboxBuilder::from_file(path).build().unwrap()); }); } diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index 165d753fd..64cd780de 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -32,7 +32,7 @@ fn interrupt_host_call() { }; with_rust_sandbox_from( - SandboxBuilder::new().host_function("Spin", spin), + |builder| builder.host_function("Spin", spin), |mut sandbox| { let snapshot = sandbox.snapshot().unwrap(); let interrupt_handle = sandbox.interrupt_handle(); @@ -294,12 +294,14 @@ fn interrupt_moved_sandbox() { #[cfg(target_os = "linux")] #[serial(thread_heavy)] fn interrupt_custom_signal_no_and_retry_delay() { - let builder = SandboxBuilder::new() - .interrupt_vcpu_sigrtmin_offset(0) - .unwrap() - .interrupt_retry_delay(Duration::from_secs(1)); + let configure = |builder: SandboxBuilder| { + builder + .interrupt_vcpu_sigrtmin_offset(0) + .unwrap() + .interrupt_retry_delay(Duration::from_secs(1)) + }; - with_rust_sandbox_from(builder, |mut sbox1| { + with_rust_sandbox_from(configure, |mut sbox1| { let snapshot1 = sbox1.snapshot().unwrap(); let interrupt_handle = sbox1.interrupt_handle(); assert!(!interrupt_handle.dropped()); // not yet dropped @@ -332,11 +334,13 @@ fn interrupt_custom_signal_no_and_retry_delay() { #[test] fn interrupt_spamming_host_call() { - let builder = SandboxBuilder::new().host_function("HostFunc1", || { - // do nothing - }); + let configure = |builder: SandboxBuilder| { + builder.host_function("HostFunc1", || { + // do nothing + }) + }; - with_rust_sandbox_from(builder, |mut sbox1| { + with_rust_sandbox_from(configure, |mut sbox1| { let interrupt_handle = sbox1.interrupt_handle(); let barrier = Arc::new(Barrier::new(2)); @@ -526,8 +530,8 @@ fn guest_malloc_abort() { "precondition: size_to_allocate ({size_to_allocate}) must be > heap_size ({heap_size})" ); - let builder = SandboxBuilder::new().heap_size(heap_size); - with_rust_sandbox_from(builder, |mut sbox2| { + let configure = |builder: SandboxBuilder| builder.heap_size(heap_size); + with_rust_sandbox_from(configure, |mut sbox2| { let err = sbox2 .call::( "CallMalloc", // uses the rust allocator to allocate a vector on heap @@ -599,8 +603,8 @@ fn corrupt_output_back_pointer_rejected() { fn guest_panic_no_alloc() { let heap_size = 0x8000; - let builder = SandboxBuilder::new().heap_size(heap_size); - with_rust_sandbox_from(builder, |mut sbox| { + let configure = |builder: SandboxBuilder| builder.heap_size(heap_size); + with_rust_sandbox_from(configure, |mut sbox| { let res = sbox .call::( "ExhaustHeap", // uses the rust allocator to allocate small blocks on the heap until OOM @@ -797,12 +801,14 @@ fn log_test_messages(levelfilter: Option) { for level in filters.iter() { // Only use Rust guest because the C guest has a different signature for LogMessage // (Long vs Int for the level parameter) - let mut builder = SandboxBuilder::new(); - if let Some(levelfilter) = levelfilter { - builder = builder.guest_log_level(levelfilter); - } + let configure = |mut builder: SandboxBuilder| { + if let Some(levelfilter) = levelfilter { + builder = builder.guest_log_level(levelfilter); + } + builder + }; - with_rust_sandbox_from(builder, |mut sbox1| { + with_rust_sandbox_from(configure, |mut sbox1| { let level: u64 = GuestLogFilter::from(*level).into(); let message = format!("Hello from log_message level {}", level as i32); sbox1 @@ -816,8 +822,9 @@ fn log_test_messages(levelfilter: Option) { /// or not #[test] fn test_if_guest_is_able_to_get_bool_return_values_from_host() { - let builder = SandboxBuilder::new().host_function("HostBool", |a: i32, b: i32| a + b > 10); - with_c_sandbox_from(builder, |mut sbox3| { + let configure = + |builder: SandboxBuilder| builder.host_function("HostBool", |a: i32, b: i32| a + b > 10); + with_c_sandbox_from(configure, |mut sbox3| { for i in 1..10 { if i < 6 { let res = sbox3 @@ -838,8 +845,9 @@ fn test_if_guest_is_able_to_get_bool_return_values_from_host() { /// or not #[test] fn test_if_guest_is_able_to_get_float_return_values_from_host() { - let builder = SandboxBuilder::new().host_function("HostAddFloat", |a: f32, b: f32| a + b); - with_c_sandbox_from(builder, |mut sbox3| { + let configure = + |builder: SandboxBuilder| builder.host_function("HostAddFloat", |a: f32, b: f32| a + b); + with_c_sandbox_from(configure, |mut sbox3| { let res = sbox3 .call::("GuestRetrievesFloatValue", (1.34_f32, 1.34_f32)) .unwrap(); @@ -851,8 +859,9 @@ fn test_if_guest_is_able_to_get_float_return_values_from_host() { /// or not #[test] fn test_if_guest_is_able_to_get_double_return_values_from_host() { - let builder = SandboxBuilder::new().host_function("HostAddDouble", |a: f64, b: f64| a + b); - with_c_sandbox_from(builder, |mut sbox3| { + let configure = + |builder: SandboxBuilder| builder.host_function("HostAddDouble", |a: f64, b: f64| a + b); + with_c_sandbox_from(configure, |mut sbox3| { let res = sbox3 .call::("GuestRetrievesDoubleValue", (1.34_f64, 1.34_f64)) .unwrap(); @@ -864,10 +873,12 @@ fn test_if_guest_is_able_to_get_double_return_values_from_host() { /// or not #[test] fn test_if_guest_is_able_to_get_string_return_values_from_host() { - let builder = SandboxBuilder::new().host_function("HostAddStrings", |a: String| { - a + ", string added by Host Function" - }); - with_c_sandbox_from(builder, |mut sbox3| { + let configure = |builder: SandboxBuilder| { + builder.host_function("HostAddStrings", |a: String| { + a + ", string added by Host Function" + }) + }; + with_c_sandbox_from(configure, |mut sbox3| { let res = sbox3 .call::("GuestRetrievesStringValue", ()) .unwrap(); @@ -1363,13 +1374,12 @@ fn interrupt_infinite_loop_stress_test() { let barrier_for_host = barrier.clone(); // Register a host function that waits on the barrier - let mut sandbox = build_rust_sandbox(SandboxBuilder::new().host_function( - "WaitForKill", - move || { + let mut sandbox = build_rust_sandbox(|builder| { + builder.host_function("WaitForKill", move || { barrier_for_host.wait(); Ok(()) - }, - )); + }) + }); // Take a snapshot to restore after each kill let snapshot = sandbox.snapshot().unwrap(); @@ -1446,11 +1456,12 @@ fn interrupt_infinite_moving_loop_stress_test() { let entered_guest_clone = entered_guest.clone(); // Register a host function that waits on the barrier - let sandbox = - build_rust_sandbox(SandboxBuilder::new().host_function("WaitForKill", move || { + let sandbox = build_rust_sandbox(|builder| { + builder.host_function("WaitForKill", move || { entered_guest.store(true, Ordering::Relaxed); Ok(()) - })); + }) + }); // These 2 sandboxes will have the same TID let bait = new_rust_sandbox(); diff --git a/src/hyperlight_host/tests/sandbox_host_tests.rs b/src/hyperlight_host/tests/sandbox_host_tests.rs index ebe943611..6bfe2e5dc 100644 --- a/src/hyperlight_host/tests/sandbox_host_tests.rs +++ b/src/hyperlight_host/tests/sandbox_host_tests.rs @@ -104,9 +104,9 @@ fn invalid_guest_function_name() { #[test] fn set_static() { with_all_guests(|path| { - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(path) .scratch_size(0x100C000) - .build_from_file(path) + .build() .unwrap(); let fn_name = "SetStatic"; let res = sandbox.call::(fn_name, ()); @@ -148,9 +148,9 @@ fn multiple_parameters() { } with_all_guests(|path| { - let mut sb = SandboxBuilder::new() + let mut sb = SandboxBuilder::from_file(path) .host_print(writer.clone()) - .build_from_file(path) + .build() .unwrap(); test_case!(sb, rx, "PrintTwoArgs", (a, b)); test_case!(sb, rx, "PrintThreeArgs", (a, b, c)); @@ -198,11 +198,11 @@ fn incorrect_parameter_num() { #[test] fn small_scratch_sandbox() { - let a = SandboxBuilder::new() + let a = SandboxBuilder::from_file(simple_guest_as_pathbuf()) .scratch_size(0x48000) .input_data_size(0x24000) .output_data_size(0x24000) - .build_from_file(simple_guest_as_pathbuf()); + .build(); assert!(matches!( a.unwrap_err(), @@ -237,9 +237,9 @@ fn simple_test_helper() { let message2 = "world"; with_all_guests(|path| { - let mut sandbox = SandboxBuilder::new() + let mut sandbox = SandboxBuilder::from_file(path) .host_print(writer.clone()) - .build_from_file(path) + .build() .unwrap(); let res: i32 = sandbox.call("PrintOutput", message.to_string()).unwrap(); assert_eq!(res, 5); @@ -291,13 +291,13 @@ fn callback_test_helper() { with_all_guests(|path| { // create host function let (tx, rx) = channel(); - let mut init_sandbox = SandboxBuilder::new() + let mut init_sandbox = SandboxBuilder::from_file(path) .host_function("HostMethod1", move |msg: String| { let len = msg.len(); tx.send(msg).unwrap(); Ok(len as i32) }) - .build_from_file(path) + .build() .unwrap(); // call guest function that calls host function @@ -336,11 +336,11 @@ fn callback_test_parallel() { fn host_function_error() { with_all_guests(|path| { // create host function - let mut init_sandbox = SandboxBuilder::new() + let mut init_sandbox = SandboxBuilder::from_file(path) .host_function("HostMethod1", |_: String| -> Result { Err(new_error!("Host function error!")) }) - .build_from_file(path) + .build() .unwrap(); // call guest function that calls host function diff --git a/src/hyperlight_host/tests/snapshot_goldens/checks.rs b/src/hyperlight_host/tests/snapshot_goldens/checks.rs index 823f712ef..06cb014bd 100644 --- a/src/hyperlight_host/tests/snapshot_goldens/checks.rs +++ b/src/hyperlight_host/tests/snapshot_goldens/checks.rs @@ -48,10 +48,10 @@ 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); - SandboxBuilder::new() + SandboxBuilder::from_snapshot(Arc::new(snap)) .host_functions(funcs) - .build_from_snapshot(Arc::new(snap)) - .map_err(|e| format!("build_from_snapshot({}): {e}", self.tag())) + .build() + .map_err(|e| format!("build({}): {e}", self.tag())) } } @@ -297,10 +297,10 @@ 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 = SandboxBuilder::new() + let mut sbox2 = SandboxBuilder::from_snapshot(Arc::new(loaded)) .host_functions(funcs) - .build_from_snapshot(Arc::new(loaded)) - .map_err(|e| format!("build_from_snapshot: {e}"))?; + .build() + .map_err(|e| format!("build: {e}"))?; let val: i32 = sbox2 .call("GetStatic", ()) .map_err(|e| format!("GetStatic on chained: {e}"))?; diff --git a/src/hyperlight_host/tests/snapshot_goldens/fixtures.rs b/src/hyperlight_host/tests/snapshot_goldens/fixtures.rs index 252e373d4..fe1606962 100644 --- a/src/hyperlight_host/tests/snapshot_goldens/fixtures.rs +++ b/src/hyperlight_host/tests/snapshot_goldens/fixtures.rs @@ -26,7 +26,7 @@ pub(crate) const CALL_COUNTER_BUMP: i32 = 42; /// silent arithmetic change in `SandboxMemoryLayout::new` shifts at /// least one region between generate-time and load-time. fn golden_builder() -> SandboxBuilder { - SandboxBuilder::new() + SandboxBuilder::from_file(simpleguest_path()) .input_data_size(64 * 1024) .output_data_size(64 * 1024) .heap_size(256 * 1024) @@ -42,7 +42,7 @@ pub(crate) fn generate() -> Arc { register_host_echo_fns(&mut funcs); let mut sbox = golden_builder() .host_functions(funcs) - .build_from_file(simpleguest_path()) + .build() .expect("build golden sandbox"); run_canonical_calls(&mut sbox); sbox.snapshot().expect("snapshot")