diff --git a/README.md b/README.md index 042ff7bf2..6ca4ec826 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,6 @@ It is followed by an example of a simple guest application using the Hyperlight ```rust use std::thread; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{MultiUseSandbox, UninitializedSandbox}; fn main() -> hyperlight_host::Result<()> { @@ -54,7 +52,7 @@ fn main() -> hyperlight_host::Result<()> { // Note: This function is unused by the guest code below, it's just here for demonstration purposes // Initialize sandbox to be able to call host functions - let mut multi_use_sandbox: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default())?; + let mut multi_use_sandbox: MultiUseSandbox = uninitialized_sandbox.evolve()?; // Call a function in the guest let message = "Hello, World! I am executing inside of a VM :)\n".to_string(); diff --git a/fuzz/fuzz_targets/guest_call.rs b/fuzz/fuzz_targets/guest_call.rs index 2e0fcee00..bc0ff163f 100644 --- a/fuzz/fuzz_targets/guest_call.rs +++ b/fuzz/fuzz_targets/guest_call.rs @@ -20,8 +20,6 @@ use std::sync::{Mutex, OnceLock}; use hyperlight_host::func::{ParameterValue, ReturnType}; use hyperlight_host::sandbox::uninitialized::GuestBinary; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{MultiUseSandbox, UninitializedSandbox}; use hyperlight_testing::simple_guest_for_fuzzing_as_string; use libfuzzer_sys::fuzz_target; @@ -38,7 +36,7 @@ fuzz_target!( ) .unwrap(); - let mu_sbox: MultiUseSandbox = u_sbox.evolve(Noop::default()).unwrap(); + let mu_sbox: MultiUseSandbox = u_sbox.evolve().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 bdd8ad760..5c72a98e5 100644 --- a/fuzz/fuzz_targets/host_call.rs +++ b/fuzz/fuzz_targets/host_call.rs @@ -20,8 +20,6 @@ use std::sync::{Mutex, OnceLock}; use hyperlight_host::func::{ParameterValue, ReturnType}; use hyperlight_host::sandbox::uninitialized::GuestBinary; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{HyperlightError, MultiUseSandbox, UninitializedSandbox}; use hyperlight_testing::simple_guest_for_fuzzing_as_string; use libfuzzer_sys::fuzz_target; @@ -37,7 +35,7 @@ fuzz_target!( ) .unwrap(); - let mu_sbox: MultiUseSandbox = u_sbox.evolve(Noop::default()).unwrap(); + let mu_sbox: MultiUseSandbox = u_sbox.evolve().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 0a7e90d40..c9c889e9e 100644 --- a/fuzz/fuzz_targets/host_print.rs +++ b/fuzz/fuzz_targets/host_print.rs @@ -3,8 +3,6 @@ use std::sync::{Mutex, OnceLock}; use hyperlight_host::sandbox::uninitialized::GuestBinary; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{MultiUseSandbox, UninitializedSandbox}; use hyperlight_testing::simple_guest_for_fuzzing_as_string; use libfuzzer_sys::{Corpus, fuzz_target}; @@ -23,7 +21,7 @@ fuzz_target!( ) .unwrap(); - let mu_sbox: MultiUseSandbox = u_sbox.evolve(Noop::default()).unwrap(); + let mu_sbox: MultiUseSandbox = u_sbox.evolve().unwrap(); SANDBOX.set(Mutex::new(mu_sbox)).unwrap(); }, diff --git a/src/hyperlight_component_util/src/host.rs b/src/hyperlight_component_util/src/host.rs index 4b822b10c..b8a194dd2 100644 --- a/src/hyperlight_component_util/src/host.rs +++ b/src/hyperlight_component_util/src/host.rs @@ -347,7 +347,6 @@ fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Com pub(crate) rt: ::std::sync::Arc<::std::sync::Mutex<#rtsid>>, } pub(crate) fn register_host_functions(sb: &mut S, i: I) -> ::std::sync::Arc<::std::sync::Mutex<#rtsid>> { - use ::hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; let rts = ::std::sync::Arc::new(::std::sync::Mutex::new(#rtsid::new())); let #import_id = ::std::sync::Arc::new(::std::sync::Mutex::new(i)); #(#imports)* @@ -357,14 +356,12 @@ fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Com #(#exports)* } impl #ns::#r#trait for ::hyperlight_host::sandbox::UninitializedSandbox { - type Exports = #wrapper_name; + type Exports = #wrapper_name; fn instantiate(mut self, i: I) -> Self::Exports { let rts = register_host_functions(&mut self, i); - let noop = ::core::default::Default::default(); - let sb = ::hyperlight_host::sandbox_state::sandbox::EvolvableSandbox::evolve(self, noop).unwrap(); - let cc = ::hyperlight_host::func::call_ctx::MultiUseGuestCallContext::start(sb); + let sb = self.evolve().unwrap(); #wrapper_name { - sb: cc, + sb, rt: rts, } } diff --git a/src/hyperlight_host/benches/benchmarks.rs b/src/hyperlight_host/benches/benchmarks.rs index c9160ff52..96cc7ecf0 100644 --- a/src/hyperlight_host/benches/benchmarks.rs +++ b/src/hyperlight_host/benches/benchmarks.rs @@ -19,8 +19,6 @@ use hyperlight_host::GuestBinary; use hyperlight_host::sandbox::{ Callable, MultiUseSandbox, SandboxConfiguration, UninitializedSandbox, }; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_testing::simple_guest_as_string; fn create_uninit_sandbox() -> UninitializedSandbox { @@ -29,7 +27,7 @@ fn create_uninit_sandbox() -> UninitializedSandbox { } fn create_multiuse_sandbox() -> MultiUseSandbox { - create_uninit_sandbox().evolve(Noop::default()).unwrap() + create_uninit_sandbox().evolve().unwrap() } fn guest_call_benchmark(c: &mut Criterion) { @@ -38,24 +36,20 @@ fn guest_call_benchmark(c: &mut Criterion) { // Benchmarks a single guest function call. // The benchmark does **not** include the time to reset the sandbox memory after the call. group.bench_function("guest_call", |b| { - let mut call_ctx = create_multiuse_sandbox().new_call_context(); + let mut sbox = create_multiuse_sandbox(); - b.iter(|| { - call_ctx - .call::("Echo", "hello\n".to_string()) - .unwrap() - }); + b.iter(|| sbox.call::("Echo", "hello\n".to_string()).unwrap()); }); // Benchmarks a single guest function call. // The benchmark does include the time to reset the sandbox memory after the call. - group.bench_function("guest_call_with_reset", |b| { - let mut sandbox = create_multiuse_sandbox(); + group.bench_function("guest_call_with_restore", |b| { + let mut sbox = create_multiuse_sandbox(); + let snapshot = sbox.snapshot().unwrap(); b.iter(|| { - sandbox - .call_guest_function_by_name::("Echo", "hello\n".to_string()) - .unwrap() + sbox.call::("Echo", "hello\n".to_string()).unwrap(); + sbox.restore(&snapshot).unwrap(); }); }); @@ -69,11 +63,13 @@ fn guest_call_benchmark(c: &mut Criterion) { .register("HostAdd", |a: i32, b: i32| Ok(a + b)) .unwrap(); - let multiuse_sandbox: MultiUseSandbox = - uninitialized_sandbox.evolve(Noop::default()).unwrap(); - let mut call_ctx = multiuse_sandbox.new_call_context(); + let mut multiuse_sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap(); - b.iter(|| call_ctx.call::("Add", (1_i32, 41_i32)).unwrap()); + b.iter(|| { + multiuse_sandbox + .call::("Add", (1_i32, 41_i32)) + .unwrap() + }); }); group.finish(); @@ -99,7 +95,7 @@ fn guest_call_benchmark_large_param(c: &mut Criterion) { Some(config), ) .unwrap(); - let mut sandbox = sandbox.evolve(Noop::default()).unwrap(); + let mut sandbox = sandbox.evolve().unwrap(); b.iter(|| { sandbox @@ -139,17 +135,6 @@ fn sandbox_benchmark(c: &mut Criterion) { b.iter(create_multiuse_sandbox); }); - // Benchmarks the time to create a new sandbox and create a new call context. - // Does **not** include the time to drop the sandbox or the call context. - group.bench_function("create_sandbox_and_call_context", |b| { - b.iter_with_large_drop(|| create_multiuse_sandbox().new_call_context()); - }); - - // Benchmarks the time to create a new sandbox, create a new call context, and drop the call context. - group.bench_function("create_sandbox_and_call_context_and_drop", |b| { - b.iter(|| create_multiuse_sandbox().new_call_context()); - }); - group.finish(); } diff --git a/src/hyperlight_host/examples/func_ctx/main.rs b/src/hyperlight_host/examples/func_ctx/main.rs index ce427a651..b7a7e3142 100644 --- a/src/hyperlight_host/examples/func_ctx/main.rs +++ b/src/hyperlight_host/examples/func_ctx/main.rs @@ -14,30 +14,26 @@ See the License for the specific language governing permissions and limitations under the License. */ -use hyperlight_host::func::call_ctx::MultiUseGuestCallContext; -use hyperlight_host::sandbox::{Callable, MultiUseSandbox, UninitializedSandbox}; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; +use hyperlight_host::sandbox::{MultiUseSandbox, UninitializedSandbox}; use hyperlight_host::{GuestBinary, Result}; use hyperlight_testing::simple_guest_as_string; fn main() { // create a new `MultiUseSandbox` configured to run the `simpleguest.exe` // test guest binary - let sbox1: MultiUseSandbox = { + let mut sbox1: MultiUseSandbox = { let path = simple_guest_as_string().unwrap(); let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap(); - u_sbox.evolve(Noop::default()) + u_sbox.evolve() } .unwrap(); // create a new call context from the sandbox, then do some calls with it. - let ctx1 = sbox1.new_call_context(); - let sbox2 = do_calls(ctx1).unwrap(); + do_calls(&mut sbox1).unwrap(); + // create a new call context from the returned sandbox, then do some calls // with that one - let ctx2 = sbox2.new_call_context(); - do_calls(ctx2).unwrap(); + do_calls(&mut sbox1).unwrap(); } /// Given a `MultiUseGuestCallContext` derived from an existing @@ -45,11 +41,12 @@ fn main() { /// binary, do several calls against that binary, print their results, then /// call `ctx.finish()` and return the resulting `MultiUseSandbox`. Return an `Err` /// if anything failed. -fn do_calls(mut ctx: MultiUseGuestCallContext) -> Result { - let res: String = ctx.call("Echo", "hello".to_string())?; +fn do_calls(sbox: &mut MultiUseSandbox) -> Result<()> { + let res: String = sbox.call_guest_function_by_name("Echo", "hello".to_string())?; println!("got Echo res: {res}"); - let res: i32 = ctx.call("CallMalloc", 200_i32)?; + let res: i32 = sbox.call_guest_function_by_name("CallMalloc", 200_i32)?; println!("got CallMalloc res: {res}"); - ctx.finish() + + Ok(()) } diff --git a/src/hyperlight_host/examples/guest-debugging/main.rs b/src/hyperlight_host/examples/guest-debugging/main.rs index f33ed511f..6b1f46677 100644 --- a/src/hyperlight_host/examples/guest-debugging/main.rs +++ b/src/hyperlight_host/examples/guest-debugging/main.rs @@ -19,8 +19,6 @@ use std::thread; use hyperlight_host::sandbox::SandboxConfiguration; #[cfg(gdb)] use hyperlight_host::sandbox::config::DebugInfo; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{MultiUseSandbox, UninitializedSandbox}; /// Build a sandbox configuration that enables GDB debugging when the `gdb` feature is enabled. @@ -70,9 +68,8 @@ fn main() -> hyperlight_host::Result<()> { // Note: This function is unused, it's just here for demonstration purposes // Initialize sandboxes to be able to call host functions - let mut multi_use_sandbox_dbg: MultiUseSandbox = - uninitialized_sandbox_dbg.evolve(Noop::default())?; - let mut multi_use_sandbox: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default())?; + let mut multi_use_sandbox_dbg: MultiUseSandbox = uninitialized_sandbox_dbg.evolve()?; + let mut multi_use_sandbox: MultiUseSandbox = uninitialized_sandbox.evolve()?; // Call guest function let message = diff --git a/src/hyperlight_host/examples/hello-world/main.rs b/src/hyperlight_host/examples/hello-world/main.rs index 57c0b4398..e8461954a 100644 --- a/src/hyperlight_host/examples/hello-world/main.rs +++ b/src/hyperlight_host/examples/hello-world/main.rs @@ -16,8 +16,6 @@ limitations under the License. #![allow(clippy::disallowed_macros)] use std::thread; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{MultiUseSandbox, UninitializedSandbox}; fn main() -> hyperlight_host::Result<()> { @@ -37,7 +35,7 @@ fn main() -> hyperlight_host::Result<()> { // Note: This function is unused, it's just here for demonstration purposes // Initialize sandbox to be able to call host functions - let mut multi_use_sandbox: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default())?; + let mut multi_use_sandbox: MultiUseSandbox = uninitialized_sandbox.evolve()?; // 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 ea6e18b31..470441e72 100644 --- a/src/hyperlight_host/examples/logging/main.rs +++ b/src/hyperlight_host/examples/logging/main.rs @@ -18,11 +18,8 @@ extern crate hyperlight_host; use std::sync::{Arc, Barrier}; -use hyperlight_host::sandbox::Callable; use hyperlight_host::sandbox::uninitialized::UninitializedSandbox; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; -use hyperlight_host::{GuestBinary, MultiUseSandbox, Result}; +use hyperlight_host::{GuestBinary, Result}; use hyperlight_testing::simple_guest_as_string; fn fn_writer(_msg: String) -> Result { @@ -48,10 +45,7 @@ fn main() -> Result<()> { usandbox.register_print(fn_writer)?; // Initialize the sandbox. - - let no_op = Noop::::default(); - - let mut multiuse_sandbox = usandbox.evolve(no_op)?; + let mut multiuse_sandbox = usandbox.evolve()?; // Call a guest function 5 times to generate some log entries. for _ in 0..5 { @@ -81,10 +75,7 @@ fn main() -> Result<()> { UninitializedSandbox::new(GuestBinary::FilePath(hyperlight_guest_path.clone()), None)?; // Initialize the sandbox. - - let no_op = Noop::::default(); - - let mut multiuse_sandbox = usandbox.evolve(no_op)?; + let mut multiuse_sandbox = usandbox.evolve()?; let interrupt_handle = multiuse_sandbox.interrupt_handle(); let barrier = Arc::new(Barrier::new(2)); let barrier2 = barrier.clone(); @@ -102,10 +93,10 @@ fn main() -> Result<()> { // Call a function that gets cancelled by the host function 5 times to generate some log entries. for _ in 0..NUM_CALLS { - let mut ctx = multiuse_sandbox.new_call_context(); barrier.wait(); - ctx.call::<()>("Spin", ()).unwrap_err(); - multiuse_sandbox = ctx.finish().unwrap(); + multiuse_sandbox + .call_guest_function_by_name::<()>("Spin", ()) + .unwrap_err(); } thread.join().unwrap(); diff --git a/src/hyperlight_host/examples/metrics/main.rs b/src/hyperlight_host/examples/metrics/main.rs index 5b7cc103f..4328a3bfa 100644 --- a/src/hyperlight_host/examples/metrics/main.rs +++ b/src/hyperlight_host/examples/metrics/main.rs @@ -18,11 +18,8 @@ extern crate hyperlight_host; use std::sync::{Arc, Barrier}; use std::thread::{JoinHandle, spawn}; -use hyperlight_host::sandbox::Callable; use hyperlight_host::sandbox::uninitialized::UninitializedSandbox; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; -use hyperlight_host::{GuestBinary, MultiUseSandbox, Result}; +use hyperlight_host::{GuestBinary, Result}; use hyperlight_testing::simple_guest_as_string; // Run this rust example with the flag --features "function_call_metrics" to enable more metrics to be emitted @@ -59,10 +56,7 @@ fn do_hyperlight_stuff() { usandbox.register_print(fn_writer)?; // Initialize the sandbox. - - let no_op = Noop::::default(); - - let mut multiuse_sandbox = usandbox.evolve(no_op).expect("Failed to evolve sandbox"); + let mut multiuse_sandbox = usandbox.evolve().expect("Failed to evolve sandbox"); // Call a guest function 5 times to generate some metrics. for _ in 0..5 { @@ -93,10 +87,7 @@ fn do_hyperlight_stuff() { .expect("Failed to create UninitializedSandbox"); // Initialize the sandbox. - - let no_op = Noop::::default(); - - let mut multiuse_sandbox = usandbox.evolve(no_op).expect("Failed to evolve sandbox"); + let mut multiuse_sandbox = usandbox.evolve().expect("Failed to evolve sandbox"); let interrupt_handle = multiuse_sandbox.interrupt_handle(); const NUM_CALLS: i32 = 5; @@ -116,10 +107,10 @@ fn do_hyperlight_stuff() { // Call a function that gets cancelled by the host function 5 times to generate some metrics. for _ in 0..NUM_CALLS { - let mut ctx = multiuse_sandbox.new_call_context(); barrier.wait(); - ctx.call::<()>("Spin", ()).unwrap_err(); - multiuse_sandbox = ctx.finish().unwrap(); + multiuse_sandbox + .call_guest_function_by_name::<()>("Spin", ()) + .unwrap_err(); } for join_handle in join_handles { diff --git a/src/hyperlight_host/examples/tracing-chrome/main.rs b/src/hyperlight_host/examples/tracing-chrome/main.rs index a93e03fab..259a37057 100644 --- a/src/hyperlight_host/examples/tracing-chrome/main.rs +++ b/src/hyperlight_host/examples/tracing-chrome/main.rs @@ -15,9 +15,7 @@ limitations under the License. */ #![allow(clippy::disallowed_macros)] use hyperlight_host::sandbox::uninitialized::UninitializedSandbox; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; -use hyperlight_host::{GuestBinary, MultiUseSandbox, Result}; +use hyperlight_host::{GuestBinary, Result}; use hyperlight_testing::simple_guest_as_string; use tracing_chrome::ChromeLayerBuilder; use tracing_subscriber::prelude::*; @@ -34,9 +32,7 @@ fn main() -> Result<()> { // Create a new sandbox. let usandbox = UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)?; - let mut sbox = usandbox - .evolve(Noop::::default()) - .unwrap(); + let mut sbox = usandbox.evolve().unwrap(); // 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 86b6b4e29..69d318c1f 100644 --- a/src/hyperlight_host/examples/tracing-otlp/main.rs +++ b/src/hyperlight_host/examples/tracing-otlp/main.rs @@ -26,11 +26,8 @@ use std::io::stdin; use std::sync::{Arc, Barrier, Mutex}; use std::thread::{JoinHandle, spawn}; -use hyperlight_host::sandbox::Callable; use hyperlight_host::sandbox::uninitialized::UninitializedSandbox; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; -use hyperlight_host::{GuestBinary, MultiUseSandbox, Result as HyperlightResult}; +use hyperlight_host::{GuestBinary, Result as HyperlightResult}; use hyperlight_testing::simple_guest_as_string; use opentelemetry::trace::TracerProvider; use opentelemetry::{KeyValue, global}; @@ -134,10 +131,7 @@ fn run_example(wait_input: bool) -> HyperlightResult<()> { usandbox.register_print(fn_writer)?; // Initialize the sandbox. - - let no_op = Noop::::default(); - - let mut multiuse_sandbox = usandbox.evolve(no_op)?; + let mut multiuse_sandbox = usandbox.evolve()?; // Call a guest function 5 times to generate some log entries. for _ in 0..5 { @@ -184,10 +178,10 @@ fn run_example(wait_input: bool) -> HyperlightResult<()> { uuid = %id, ); let _entered = span.enter(); - let mut ctx = multiuse_sandbox.new_call_context(); barrier.wait(); - ctx.call::<()>("Spin", ()).unwrap_err(); - multiuse_sandbox = ctx.finish().unwrap(); + multiuse_sandbox + .call_guest_function_by_name::<()>("Spin", ()) + .unwrap_err(); } thread.join().expect("Thread panicked"); } diff --git a/src/hyperlight_host/examples/tracing-tracy/main.rs b/src/hyperlight_host/examples/tracing-tracy/main.rs index 615b59c38..03867f0d5 100644 --- a/src/hyperlight_host/examples/tracing-tracy/main.rs +++ b/src/hyperlight_host/examples/tracing-tracy/main.rs @@ -15,9 +15,7 @@ limitations under the License. */ #![allow(clippy::disallowed_macros)] use hyperlight_host::sandbox::uninitialized::UninitializedSandbox; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; -use hyperlight_host::{GuestBinary, MultiUseSandbox, Result}; +use hyperlight_host::{GuestBinary, Result}; use hyperlight_testing::simple_guest_as_string; use tracing_subscriber::EnvFilter; use tracing_subscriber::layer::SubscriberExt; @@ -40,9 +38,7 @@ fn main() -> Result<()> { // Create a new sandbox. let usandbox = UninitializedSandbox::new(GuestBinary::FilePath(simple_guest_path), None)?; - let mut sbox = usandbox - .evolve(Noop::::default()) - .unwrap(); + let mut sbox = usandbox.evolve().unwrap(); // do the function call let current_time = std::time::Instant::now(); diff --git a/src/hyperlight_host/examples/tracing/main.rs b/src/hyperlight_host/examples/tracing/main.rs index dfc680576..66020dcfa 100644 --- a/src/hyperlight_host/examples/tracing/main.rs +++ b/src/hyperlight_host/examples/tracing/main.rs @@ -19,11 +19,8 @@ extern crate hyperlight_host; use std::sync::{Arc, Barrier}; use std::thread::{JoinHandle, spawn}; -use hyperlight_host::sandbox::Callable; use hyperlight_host::sandbox::uninitialized::UninitializedSandbox; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; -use hyperlight_host::{GuestBinary, MultiUseSandbox, Result}; +use hyperlight_host::{GuestBinary, Result}; use hyperlight_testing::simple_guest_as_string; use tracing_forest::ForestLayer; use tracing_subscriber::layer::SubscriberExt; @@ -76,10 +73,7 @@ fn run_example() -> Result<()> { usandbox.register_print(fn_writer)?; // Initialize the sandbox. - - let no_op = Noop::::default(); - - let mut multiuse_sandbox = usandbox.evolve(no_op)?; + let mut multiuse_sandbox = usandbox.evolve()?; // Call a guest function 5 times to generate some log entries. for _ in 0..5 { @@ -108,10 +102,7 @@ fn run_example() -> Result<()> { UninitializedSandbox::new(GuestBinary::FilePath(hyperlight_guest_path.clone()), None)?; // Initialize the sandbox. - - let no_op = Noop::::default(); - - let mut multiuse_sandbox = usandbox.evolve(no_op)?; + let mut multiuse_sandbox = usandbox.evolve()?; let interrupt_handle = multiuse_sandbox.interrupt_handle(); // Call a function that gets cancelled by the host function 5 times to generate some log entries. @@ -139,10 +130,10 @@ fn run_example() -> Result<()> { uuid = %id, ); let _entered = span.enter(); - let mut ctx = multiuse_sandbox.new_call_context(); barrier.wait(); - ctx.call::<()>("Spin", ()).unwrap_err(); - multiuse_sandbox = ctx.finish().unwrap(); + multiuse_sandbox + .call_guest_function_by_name::<()>("Spin", ()) + .unwrap_err(); } for join_handle in join_handles { diff --git a/src/hyperlight_host/src/func/call_ctx.rs b/src/hyperlight_host/src/func/call_ctx.rs deleted file mode 100644 index 168437b97..000000000 --- a/src/hyperlight_host/src/func/call_ctx.rs +++ /dev/null @@ -1,279 +0,0 @@ -/* -Copyright 2025 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use tracing::{Span, instrument}; - -use super::{ParameterTuple, SupportedReturnType}; -use crate::mem::memory_region::MemoryRegion; -use crate::sandbox::Callable; -use crate::{MultiUseSandbox, Result}; -/// A context for calling guest functions. -/// -/// Takes ownership of an existing `MultiUseSandbox`. -/// Once created, guest function calls may be made through this and only this context -/// until it is converted back to the `MultiUseSandbox` from which it originated. -/// -/// Upon this conversion,the memory associated with the `MultiUseSandbox` it owns will be reset to the state it was in before -/// this context was created. -/// -/// Calls made through this context will cause state to be retained across calls, until such time as the context -/// is converted back to a `MultiUseSandbox` -/// -/// If dropped, the `MultiUseSandbox` from which it came will be also be dropped as it is owned by the -/// `MultiUseGuestCallContext` until it is converted back to a `MultiUseSandbox` -/// -#[derive(Debug)] -pub struct MultiUseGuestCallContext { - sbox: MultiUseSandbox, -} - -impl MultiUseGuestCallContext { - /// Take ownership of a `MultiUseSandbox` and - /// return a new `MultiUseGuestCallContext` instance. - /// - #[instrument(skip_all, parent = Span::current())] - pub fn start(sbox: MultiUseSandbox) -> Self { - Self { sbox } - } - - /// Close out the context and get back the internally-stored - /// `MultiUseSandbox`. Future contexts opened by the returned sandbox - /// will have guest state restored. - #[instrument(err(Debug), skip(self), parent = Span::current())] - pub fn finish(mut self) -> Result { - self.sbox.restore_state()?; - Ok(self.sbox) - } - /// Close out the context and get back the internally-stored - /// `MultiUseSandbox`. - /// - /// Note that this method is pub(crate) and does not reset the state of the - /// sandbox. - /// - /// It is intended to be used when evolving a MultiUseSandbox to a new state - /// and is not intended to be called publicly. It allows the state of the guest to be altered - /// during the evolution of one sandbox state to another, enabling the new state created - /// to be captured and stored in the Sandboxes state stack. - /// - pub(crate) fn finish_no_reset(self) -> MultiUseSandbox { - self.sbox - } - - /// Map a region of host memory into the sandbox. - /// - /// Depending on the host platform, there are likely alignment - /// requirements of at least one page for base and len. - /// - /// `rgn.region_type` is ignored, since guest PTEs are not created - /// for the new memory. - /// - /// # Safety - /// It is the caller's responsibility to ensure that the host side - /// of the region remains intact and is not written to until this - /// mapping is removed, either due to the destruction of the - /// sandbox or due to a state rollback - pub unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()> { - unsafe { self.sbox.map_region(rgn) } - } - - /// Map the contents of a file into the guest at a particular address - /// - /// Returns the length of the mapping - pub fn map_file_cow(&mut self, fp: &std::path::Path, guest_base: u64) -> Result { - self.sbox.map_file_cow(fp, guest_base) - } -} - -impl Callable for MultiUseGuestCallContext { - /// Call the guest function called `func_name` with the given arguments - /// `args`, and expect the return value have the same type as - /// `func_ret_type`. - /// - /// Every call to a guest function through this method will be made with the same "context" - /// meaning that the guest state resulting from any previous call will be present/osbservable - /// by the guest function called. - /// - /// If you want to reset state, call `finish()` on this `MultiUseGuestCallContext` - /// and get a new one from the resulting `MultiUseSandbox` - #[instrument(err(Debug),skip(self, args),parent = Span::current())] - fn call( - &mut self, - func_name: &str, - args: impl ParameterTuple, - ) -> Result { - // we are guaranteed to be holding a lock now, since `self` can't - // exist without doing so. Since GuestCallContext is effectively - // !Send (and !Sync), we also don't need to worry about - // synchronization - - let ret = self.sbox.call_guest_function_by_name_no_reset( - func_name, - Output::TYPE, - args.into_value(), - ); - Output::from_value(ret?) - } -} - -#[cfg(test)] -mod tests { - use std::sync::mpsc::sync_channel; - use std::thread::{self, JoinHandle}; - - use hyperlight_testing::simple_guest_as_string; - - use super::MultiUseGuestCallContext; - use crate::sandbox::Callable; - use crate::sandbox_state::sandbox::EvolvableSandbox; - use crate::sandbox_state::transition::Noop; - use crate::{GuestBinary, HyperlightError, MultiUseSandbox, Result, UninitializedSandbox}; - - fn new_uninit() -> Result { - let path = simple_guest_as_string().map_err(|e| { - HyperlightError::Error(format!("failed to get simple guest path ({e:?})")) - })?; - UninitializedSandbox::new(GuestBinary::FilePath(path), None) - } - - /// Test to create a `MultiUseSandbox`, then call several guest functions - /// on it across different threads. - /// - /// This test works by passing messages between threads using Rust's - /// [mpsc crate](https://doc.rust-lang.org/std/sync/mpsc). Details of this - /// interaction are as follows. - /// - /// One thread acts as the receiver (AKA: consumer) and owns the - /// `MultiUseSandbox`. This receiver fields requests from N senders - /// (AKA: producers) to make batches of calls. - /// - /// Upon receipt of a message to execute a batch, a new - /// `MultiUseGuestCallContext` is created in the receiver thread from the - /// existing `MultiUseSandbox`, and the batch is executed. - /// - /// After the batch is complete, the `MultiUseGuestCallContext` is done - /// and it is converted back to the underlying `MultiUseSandbox` - #[test] - fn test_multi_call_multi_thread() { - let (snd, recv) = sync_channel::>(0); - - // create new receiver thread and on it, begin listening for - // requests to execute batches of calls - let recv_hdl = thread::spawn(move || { - let mut sbox: MultiUseSandbox = new_uninit().unwrap().evolve(Noop::default()).unwrap(); - while let Ok(calls) = recv.recv() { - let mut ctx = sbox.new_call_context(); - for call in calls { - call.call(&mut ctx); - } - sbox = ctx.finish().unwrap(); - } - }); - - // create new sender threads - let send_handles: Vec> = (0..10) - .map(|i| { - let sender = snd.clone(); - thread::spawn(move || { - let calls = vec![ - TestFuncCall::new(move |ctx| { - let msg = format!("Hello {}", i); - let ret: String = ctx.call("Echo", msg.clone()).unwrap(); - assert_eq!(ret, msg) - }), - TestFuncCall::new(move |ctx| { - let ret: i32 = ctx.call("CallMalloc", i + 2).unwrap(); - assert_eq!(ret, i + 2) - }), - ]; - sender.send(calls).unwrap(); - }) - }) - .collect(); - - for hdl in send_handles { - hdl.join().unwrap(); - } - // after all sender threads are done, drop the sender itself - // so the receiver thread can exit. then, ensure the receiver - // thread has exited. - drop(snd); - recv_hdl.join().unwrap(); - } - - pub struct TestSandbox { - sandbox: MultiUseSandbox, - } - - impl TestSandbox { - pub fn new() -> Self { - let sbox: MultiUseSandbox = new_uninit().unwrap().evolve(Noop::default()).unwrap(); - Self { sandbox: sbox } - } - pub fn call_add_to_static_multiple_times(mut self, i: i32) -> Result { - let mut ctx = self.sandbox.new_call_context(); - let mut sum: i32 = 0; - for n in 0..i { - let result = ctx.call::("AddToStatic", n); - sum += n; - println!("{:?}", result); - let result = result.unwrap(); - assert_eq!(result, sum); - } - let result = ctx.finish(); - assert!(result.is_ok()); - self.sandbox = result.unwrap(); - Ok(self) - } - - pub fn call_add_to_static(mut self, i: i32) -> Result<()> { - for n in 0..i { - let result = self - .sandbox - .call_guest_function_by_name::("AddToStatic", n); - println!("{:?}", result); - let result = result.unwrap(); - assert_eq!(result, n); - } - Ok(()) - } - } - - #[test] - fn ensure_multiusesandbox_multi_calls_dont_reset_state() { - let sandbox = TestSandbox::new(); - let result = sandbox.call_add_to_static_multiple_times(5); - assert!(result.is_ok()); - } - - #[test] - fn ensure_multiusesandbox_single_calls_do_reset_state() { - let sandbox = TestSandbox::new(); - let result = sandbox.call_add_to_static(5); - assert!(result.is_ok()); - } - - struct TestFuncCall(Box); - - impl TestFuncCall { - fn new(f: impl FnOnce(&mut MultiUseGuestCallContext) + Send + 'static) -> Self { - TestFuncCall(Box::new(f)) - } - - fn call(self, ctx: &mut MultiUseGuestCallContext) { - (self.0)(ctx); - } - } -} diff --git a/src/hyperlight_host/src/func/mod.rs b/src/hyperlight_host/src/func/mod.rs index a0efe113e..57b69dbc4 100644 --- a/src/hyperlight_host/src/func/mod.rs +++ b/src/hyperlight_host/src/func/mod.rs @@ -14,10 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -/// Context structures used to allow the user to call one or more guest -/// functions on the same Hyperlight sandbox instance, all from within the -/// same state and mutual exclusion context. -pub mod call_ctx; /// Functionality to check for errors after a guest call pub(crate) mod guest_err; /// Definitions and functionality to enable guest-to-host function calling, diff --git a/src/hyperlight_host/src/lib.rs b/src/hyperlight_host/src/lib.rs index 2c52729a5..6e5e67816 100644 --- a/src/hyperlight_host/src/lib.rs +++ b/src/hyperlight_host/src/lib.rs @@ -58,9 +58,6 @@ pub mod metrics; /// outside this file. Types from this module needed for public consumption are /// re-exported below. pub mod sandbox; -/// `trait`s and other functionality for dealing with defining sandbox -/// states and moving between them -pub mod sandbox_state; #[cfg(all(feature = "seccomp", target_os = "linux"))] pub(crate) mod seccomp; /// Signal handling for Linux @@ -85,9 +82,6 @@ pub use sandbox::is_hypervisor_present; /// The re-export for the `GuestBinary` type pub use sandbox::uninitialized::GuestBinary; -/// The re-export for the `MultiUseGuestCallContext` type` -pub use crate::func::call_ctx::MultiUseGuestCallContext; - /// The universal `Result` type used throughout the Hyperlight codebase. pub type Result = core::result::Result; diff --git a/src/hyperlight_host/src/mem/mgr.rs b/src/hyperlight_host/src/mem/mgr.rs index 90cb76573..c7bab4ad3 100644 --- a/src/hyperlight_host/src/mem/mgr.rs +++ b/src/hyperlight_host/src/mem/mgr.rs @@ -15,7 +15,6 @@ limitations under the License. */ use std::cmp::Ordering; -use std::sync::{Arc, Mutex}; use hyperlight_common::flatbuffer_wrappers::function_call::{ FunctionCall, validate_guest_function_call_buffer, @@ -34,7 +33,6 @@ use super::ptr::{GuestPtr, RawPtr}; use super::ptr_offset::Offset; use super::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, HostSharedMemory, SharedMemory}; use super::shared_mem_snapshot::SharedMemorySnapshot; -use crate::HyperlightError::NoMemorySnapshot; use crate::sandbox::SandboxConfiguration; use crate::sandbox::uninitialized::GuestBlob; use crate::{Result, log_then_return, new_error}; @@ -75,9 +73,6 @@ pub(crate) struct SandboxMemoryManager { pub(crate) entrypoint_offset: Offset, /// How many memory regions were mapped after sandbox creation pub(crate) mapped_rgns: u64, - /// A vector of memory snapshots that can be used to save and restore the state of the memory - /// This is used by the Rust Sandbox implementation (rather than the mem_snapshot field above which only exists to support current C API) - snapshots: Arc>>, } impl SandboxMemoryManager @@ -98,7 +93,6 @@ where load_addr, entrypoint_offset, mapped_rgns: 0, - snapshots: Arc::new(Mutex::new(Vec::new())), } } @@ -265,56 +259,28 @@ where } } - /// this function will create a memory snapshot and push it onto the stack of snapshots - /// It should be used when you want to save the state of the memory, for example, when evolving a sandbox to a new state - pub(crate) fn push_state(&mut self) -> Result<()> { - let snapshot = SharedMemorySnapshot::new(&mut self.shared_mem, self.mapped_rgns)?; - self.snapshots - .try_lock() - .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .push(snapshot); - Ok(()) + pub(crate) fn snapshot(&mut self) -> Result { + SharedMemorySnapshot::new(&mut self.shared_mem, self.mapped_rgns) } - /// this function restores a memory snapshot from the last snapshot in the list but does not pop the snapshot - /// off the stack - /// It should be used when you want to restore the state of the memory to a previous state but still want to - /// retain that state, for example after calling a function in the guest + /// This function restores a memory snapshot the given snapshot. /// /// Returns the number of memory regions mapped into the sandbox /// that need to be unmapped in order for the restore to be /// completed. - pub(crate) fn restore_state_from_last_snapshot(&mut self) -> Result { - let mut snapshots = self - .snapshots - .try_lock() - .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))?; - let last = snapshots.last_mut(); - if last.is_none() { - log_then_return!(NoMemorySnapshot); + pub(crate) fn restore_snapshot(&mut self, snapshot: &SharedMemorySnapshot) -> Result { + if self.shared_mem.mem_size() != snapshot.mem_size() { + return Err(new_error!( + "Snapshot size does not match current memory size: {} != {}", + self.shared_mem.raw_mem_size(), + snapshot.mem_size() + )); } - #[allow(clippy::unwrap_used)] // We know that last is not None because we checked it above - let snapshot = last.unwrap(); let old_rgns = self.mapped_rgns; self.mapped_rgns = snapshot.restore_from_snapshot(&mut self.shared_mem)?; Ok(old_rgns - self.mapped_rgns) } - /// this function pops the last snapshot off the stack and restores the memory to the previous state - /// It should be used when you want to restore the state of the memory to a previous state and do not need to retain that state - /// for example when devolving a sandbox to a previous state. - pub(crate) fn pop_and_restore_state_from_snapshot(&mut self) -> Result { - let last = self - .snapshots - .try_lock() - .map_err(|e| new_error!("Error locking at {}:{}: {}", file!(), line!(), e))? - .pop(); - if last.is_none() { - log_then_return!(NoMemorySnapshot); - } - self.restore_state_from_last_snapshot() - } - /// Sets `addr` to the correct offset in the memory referenced by /// `shared_mem` to indicate the address of the outb pointer and context /// for calling outb function @@ -440,7 +406,6 @@ impl SandboxMemoryManager { load_addr: self.load_addr.clone(), entrypoint_offset: self.entrypoint_offset, mapped_rgns: 0, - snapshots: Arc::new(Mutex::new(Vec::new())), }, SandboxMemoryManager { shared_mem: gshm, @@ -448,7 +413,6 @@ impl SandboxMemoryManager { load_addr: self.load_addr.clone(), entrypoint_offset: self.entrypoint_offset, mapped_rgns: 0, - snapshots: Arc::new(Mutex::new(Vec::new())), }, ) } @@ -555,4 +519,25 @@ impl SandboxMemoryManager { self.layout.sandbox_memory_config.get_output_data_size(), ) } + + pub(crate) fn clear_io_buffers(&mut self) { + // Clear the output data buffer + loop { + let Ok(_) = self.shared_mem.try_pop_buffer_into::>( + self.layout.output_data_buffer_offset, + self.layout.sandbox_memory_config.get_output_data_size(), + ) else { + break; + }; + } + // Clear the input data buffer + loop { + let Ok(_) = self.shared_mem.try_pop_buffer_into::>( + self.layout.input_data_buffer_offset, + self.layout.sandbox_memory_config.get_input_data_size(), + ) else { + break; + }; + } + } } diff --git a/src/hyperlight_host/src/mem/shared_mem_snapshot.rs b/src/hyperlight_host/src/mem/shared_mem_snapshot.rs index ac2bdc6b5..b7f461716 100644 --- a/src/hyperlight_host/src/mem/shared_mem_snapshot.rs +++ b/src/hyperlight_host/src/mem/shared_mem_snapshot.rs @@ -22,7 +22,7 @@ use crate::Result; /// A wrapper around a `SharedMemory` reference and a snapshot /// of the memory therein #[derive(Clone)] -pub(super) struct SharedMemorySnapshot { +pub(crate) struct SharedMemorySnapshot { snapshot: Vec, /// How many non-main-RAM regions were mapped when this snapshot was taken? mapped_rgns: u64, @@ -53,13 +53,16 @@ impl SharedMemorySnapshot { /// Copy the memory from the internally-stored memory snapshot /// into the internally-stored `SharedMemory` #[instrument(err(Debug), skip_all, parent = Span::current(), level= "Trace")] - pub(super) fn restore_from_snapshot( - &mut self, - shared_mem: &mut S, - ) -> Result { + pub(super) fn restore_from_snapshot(&self, shared_mem: &mut S) -> Result { shared_mem.with_exclusivity(|e| e.copy_from_slice(self.snapshot.as_slice(), 0))??; Ok(self.mapped_rgns) } + + /// Return the size of the snapshot in bytes. + #[instrument(skip_all, parent = Span::current(), level= "Trace")] + pub(super) fn mem_size(&self) -> usize { + self.snapshot.len() + } } #[cfg(test)] diff --git a/src/hyperlight_host/src/metrics/mod.rs b/src/hyperlight_host/src/metrics/mod.rs index 3181d3b89..a2f4026e5 100644 --- a/src/hyperlight_host/src/metrics/mod.rs +++ b/src/hyperlight_host/src/metrics/mod.rs @@ -93,8 +93,6 @@ mod tests { use metrics_util::CompositeKey; use super::*; - use crate::sandbox_state::sandbox::EvolvableSandbox; - use crate::sandbox_state::transition::Noop; use crate::{GuestBinary, UninitializedSandbox}; #[test] @@ -108,7 +106,7 @@ mod tests { ) .unwrap(); - let mut multi = uninit.evolve(Noop::default()).unwrap(); + let mut multi = uninit.evolve().unwrap(); let interrupt_handle = multi.interrupt_handle(); // interrupt the guest function call to "Spin" after 1 second diff --git a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs index c2959262b..fd42f53d2 100644 --- a/src/hyperlight_host/src/sandbox/initialized_multi_use.rs +++ b/src/hyperlight_host/src/sandbox/initialized_multi_use.rs @@ -28,8 +28,8 @@ use hyperlight_common::flatbuffer_wrappers::function_types::{ use tracing::{Span, instrument}; use super::host_funcs::FunctionRegistry; -use super::{MemMgrWrapper, WrapperGetter}; -use crate::func::call_ctx::MultiUseGuestCallContext; +use super::snapshot::Snapshot; +use super::{Callable, MemMgrWrapper, WrapperGetter}; use crate::func::guest_err::check_for_guest_error; use crate::func::{ParameterTuple, SupportedReturnType}; #[cfg(gdb)] @@ -42,8 +42,6 @@ use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags}; use crate::mem::ptr::RawPtr; use crate::mem::shared_mem::HostSharedMemory; use crate::metrics::maybe_time_and_emit_guest_call; -use crate::sandbox_state::sandbox::{DevolvableSandbox, EvolvableSandbox, Sandbox}; -use crate::sandbox_state::transition::{MultiUseContextCallback, Noop}; use crate::{HyperlightError, Result, log_then_return}; /// A sandbox that supports being used Multiple times. @@ -94,76 +92,26 @@ impl MultiUseSandbox { } } - /// Create a new `MultiUseCallContext` suitable for making 0 or more - /// calls to guest functions within the same context. - /// - /// Since this function consumes `self`, the returned - /// `MultiUseGuestCallContext` is guaranteed mutual exclusion for calling - /// functions within the sandbox. This guarantee is enforced at compile - /// time, and no locks, atomics, or any other mutual exclusion mechanisms - /// are used at runtime. - /// - /// If you have called this function, have a `MultiUseGuestCallContext`, - /// and wish to "return" it to a `MultiUseSandbox`, call the `finish` - /// method on the context. - /// - /// Example usage (compiled as a "no_run" doctest since the test binary - /// will not be found): - /// - /// ```no_run - /// use hyperlight_host::sandbox::{UninitializedSandbox, MultiUseSandbox}; - /// use hyperlight_common::flatbuffer_wrappers::function_types::{ReturnType, ParameterValue, ReturnValue}; - /// use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; - /// use hyperlight_host::sandbox_state::transition::Noop; - /// use hyperlight_host::GuestBinary; - /// - /// // First, create a new uninitialized sandbox, then evolve it to become - /// // an initialized, single-use one. - /// let u_sbox = UninitializedSandbox::new( - /// GuestBinary::FilePath("some_guest_binary".to_string()), - /// None, - /// ).unwrap(); - /// let sbox: MultiUseSandbox = u_sbox.evolve(Noop::default()).unwrap(); - /// // Next, create a new call context from the single-use sandbox. - /// // After this line, your code will not compile if you try to use the - /// // original `sbox` variable. - /// let mut ctx = sbox.new_call_context(); - /// - /// // Do a guest call with the context. Assumes that the loaded binary - /// // ("some_guest_binary") has a function therein called "SomeGuestFunc" - /// // that takes a single integer argument and returns an integer. - /// match ctx.call( - /// "SomeGuestFunc", - /// ReturnType::Int, - /// Some(vec![ParameterValue::Int(1)]) - /// ) { - /// Ok(ReturnValue::Int(i)) => println!( - /// "got successful return value {}", - /// i, - /// ), - /// other => panic!( - /// "failed to get return value as expected ({:?})", - /// other, - /// ), - /// }; - /// // You can make further calls with the same context if you want. - /// // Otherwise, `ctx` will be dropped and all resources, including the - /// // underlying `MultiUseSandbox`, will be released and no further - /// // contexts can be created from that sandbox. - /// // - /// // If you want to avoid - /// // that behavior, call `finish` to convert the context back to - /// // the original `MultiUseSandbox`, as follows: - /// let _orig_sbox = ctx.finish(); - /// // Now, you can operate on the original sandbox again (i.e. add more - /// // host functions etc...), create new contexts, and so on. - /// ``` - #[instrument(skip_all, parent = Span::current())] - pub fn new_call_context(self) -> MultiUseGuestCallContext { - MultiUseGuestCallContext::start(self) + /// Create a snapshot of the current state of the sandbox's memory. + #[instrument(err(Debug), skip_all, parent = Span::current())] + pub fn snapshot(&mut self) -> Result { + let snapshot = self.mem_mgr.unwrap_mgr_mut().snapshot()?; + Ok(Snapshot { inner: snapshot }) + } + + /// Restore the sandbox's memory to the state captured in the given snapshot. + #[instrument(err(Debug), skip_all, parent = Span::current())] + pub fn restore(&mut self, snapshot: &Snapshot) -> Result<()> { + let rgns_to_unmap = self + .mem_mgr + .unwrap_mgr_mut() + .restore_snapshot(&snapshot.inner)?; + unsafe { self.vm.unmap_regions(rgns_to_unmap)? }; + Ok(()) } /// Call a guest function by name, with the given return type and arguments. + /// The changes made to the sandbox are persisted #[instrument(err(Debug), skip(self, args), parent = Span::current())] pub fn call_guest_function_by_name( &mut self, @@ -176,7 +124,6 @@ impl MultiUseSandbox { Output::TYPE, args.into_value(), ); - self.restore_state()?; Output::from_value(ret?) }) } @@ -260,56 +207,52 @@ impl MultiUseSandbox { args: Vec, ) -> Result { maybe_time_and_emit_guest_call(func_name, || { - let ret = self.call_guest_function_by_name_no_reset(func_name, ret_type, args); - self.restore_state()?; - ret + self.call_guest_function_by_name_no_reset(func_name, ret_type, args) }) } - /// Restore the Sandbox's state - #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] - pub(crate) fn restore_state(&mut self) -> Result<()> { - let mem_mgr = self.mem_mgr.unwrap_mgr_mut(); - let rgns_to_unmap = mem_mgr.restore_state_from_last_snapshot()?; - unsafe { self.vm.unmap_regions(rgns_to_unmap)? }; - Ok(()) - } - - pub(crate) fn call_guest_function_by_name_no_reset( + fn call_guest_function_by_name_no_reset( &mut self, function_name: &str, return_type: ReturnType, args: Vec, ) -> Result { - let fc = FunctionCall::new( - function_name.to_string(), - Some(args), - FunctionCallType::Guest, - return_type, - ); - - let buffer: Vec = fc - .try_into() - .map_err(|_| HyperlightError::Error("Failed to serialize FunctionCall".to_string()))?; - - self.get_mgr_wrapper_mut() - .as_mut() - .write_guest_function_call(&buffer)?; - - self.vm.dispatch_call_from_host( - self.dispatch_ptr.clone(), - self.out_hdl.clone(), - self.mem_hdl.clone(), - #[cfg(gdb)] - self.dbg_mem_access_fn.clone(), - )?; + let res = (|| { + let fc = FunctionCall::new( + function_name.to_string(), + Some(args), + FunctionCallType::Guest, + return_type, + ); + + let buffer: Vec = fc.try_into().map_err(|_| { + HyperlightError::Error("Failed to serialize FunctionCall".to_string()) + })?; + + self.get_mgr_wrapper_mut() + .as_mut() + .write_guest_function_call(&buffer)?; + + self.vm.dispatch_call_from_host( + self.dispatch_ptr.clone(), + self.out_hdl.clone(), + self.mem_hdl.clone(), + #[cfg(gdb)] + self.dbg_mem_access_fn.clone(), + )?; - self.check_stack_guard()?; - check_for_guest_error(self.get_mgr_wrapper_mut())?; + self.mem_mgr.check_stack_guard()?; + check_for_guest_error(self.get_mgr_wrapper_mut())?; - self.get_mgr_wrapper_mut() - .as_mut() - .get_guest_function_call_result() + self.get_mgr_wrapper_mut() + .as_mut() + .get_guest_function_call_result() + })(); + + // TODO: Do we want to allow re-entrant guest function calls? + self.get_mgr_wrapper_mut().as_mut().clear_io_buffers(); + + res } /// Get a handle to the interrupt handler for this sandbox, @@ -319,6 +262,16 @@ impl MultiUseSandbox { } } +impl Callable for MultiUseSandbox { + fn call( + &mut self, + func_name: &str, + args: impl ParameterTuple, + ) -> Result { + self.call_guest_function_by_name(func_name, args) + } +} + impl WrapperGetter for MultiUseSandbox { fn get_mgr_wrapper(&self) -> &MemMgrWrapper { &self.mem_mgr @@ -328,12 +281,6 @@ impl WrapperGetter for MultiUseSandbox { } } -impl Sandbox for MultiUseSandbox { - fn check_stack_guard(&self) -> Result { - self.mem_mgr.check_stack_guard() - } -} - impl std::fmt::Debug for MultiUseSandbox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("MultiUseSandbox") @@ -342,58 +289,6 @@ impl std::fmt::Debug for MultiUseSandbox { } } -impl DevolvableSandbox> - for MultiUseSandbox -{ - /// Consume `self` and move it back to a `MultiUseSandbox` with previous state. - /// - /// The purpose of this function is to allow multiple states to be associated with a single MultiUseSandbox. - /// - /// An implementation such as HyperlightJs or HyperlightWasm can use this to call guest functions to load JS or WASM code and then evolve the sandbox causing state to be captured. - /// The new MultiUseSandbox can then be used to call guest functions to execute the loaded code. - /// The devolve can be used to return the MultiUseSandbox to the state before the code was loaded. Thus avoiding initialisation overhead - #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] - fn devolve(mut self, _tsn: Noop) -> Result { - let rgns_to_unmap = self - .mem_mgr - .unwrap_mgr_mut() - .pop_and_restore_state_from_snapshot()?; - unsafe { self.vm.unmap_regions(rgns_to_unmap)? }; - Ok(self) - } -} - -impl<'a, F> - EvolvableSandbox< - MultiUseSandbox, - MultiUseSandbox, - MultiUseContextCallback<'a, MultiUseSandbox, F>, - > for MultiUseSandbox -where - F: FnOnce(&mut MultiUseGuestCallContext) -> Result<()> + 'a, -{ - /// The purpose of this function is to allow multiple states to be associated with a single MultiUseSandbox. - /// - /// An implementation such as HyperlightJs or HyperlightWasm can use this to call guest functions to load JS or WASM code and then evolve the sandbox causing state to be captured. - /// The new MultiUseSandbox can then be used to call guest functions to execute the loaded code. - /// - /// The evolve function creates a new MultiUseCallContext which is then passed to a callback function allowing the - /// callback function to call guest functions as part of the evolve process, once the callback function is complete - /// the context is finished using a crate internal method that does not restore the prior state of the Sandbox. - /// It then creates a mew memory snapshot on the snapshot stack and returns the MultiUseSandbox - #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] - fn evolve( - self, - transition_func: MultiUseContextCallback<'a, MultiUseSandbox, F>, - ) -> Result { - let mut ctx = self.new_call_context(); - transition_func.call(&mut ctx)?; - let mut sbox = ctx.finish_no_reset(); - sbox.mem_mgr.unwrap_mgr_mut().push_state()?; - Ok(sbox) - } -} - #[cfg(test)] mod tests { use std::sync::{Arc, Barrier}; @@ -401,14 +296,11 @@ mod tests { use hyperlight_testing::simple_guest_as_string; - use crate::func::call_ctx::MultiUseGuestCallContext; #[cfg(target_os = "linux")] use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags, MemoryRegionType}; #[cfg(target_os = "linux")] use crate::mem::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, SharedMemory as _}; use crate::sandbox::{Callable, SandboxConfiguration}; - use crate::sandbox_state::sandbox::{DevolvableSandbox, EvolvableSandbox}; - use crate::sandbox_state::transition::{MultiUseContextCallback, Noop}; use crate::{GuestBinary, HyperlightError, MultiUseSandbox, Result, UninitializedSandbox}; // Tests to ensure that many (1000) function calls can be made in a call context with a small stack (1K) and heap(14K). @@ -419,58 +311,55 @@ mod tests { cfg.set_heap_size(20 * 1024); cfg.set_stack_size(16 * 1024); - let sbox1: MultiUseSandbox = { + let mut sbox1: MultiUseSandbox = { let path = simple_guest_as_string().unwrap(); let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), Some(cfg)).unwrap(); - u_sbox.evolve(Noop::default()) + u_sbox.evolve() } .unwrap(); - let mut ctx = sbox1.new_call_context(); - for _ in 0..1000 { - ctx.call::("Echo", "hello".to_string()).unwrap(); + sbox1.call::("Echo", "hello".to_string()).unwrap(); } - let sbox2: MultiUseSandbox = { + let mut sbox2: MultiUseSandbox = { let path = simple_guest_as_string().unwrap(); let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), Some(cfg)).unwrap(); - u_sbox.evolve(Noop::default()) + u_sbox.evolve() } .unwrap(); - let mut ctx = sbox2.new_call_context(); - for i in 0..1000 { - ctx.call::( - "PrintUsingPrintf", - format!("Hello World {}\n", i).to_string(), - ) - .unwrap(); + sbox2 + .call::( + "PrintUsingPrintf", + format!("Hello World {}\n", i).to_string(), + ) + .unwrap(); } } /// Tests that evolving from MultiUseSandbox to MultiUseSandbox creates a new state - /// and devolving from MultiUseSandbox to MultiUseSandbox restores the previous state + /// and restoring a snapshot from before evolving restores the previous state #[test] - fn evolve_devolve_handles_state_correctly() { - let sbox1: MultiUseSandbox = { + fn snapshot_evolve_restore_handles_state_correctly() { + let mut sbox: MultiUseSandbox = { let path = simple_guest_as_string().unwrap(); let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap(); - u_sbox.evolve(Noop::default()) + u_sbox.evolve() } .unwrap(); - let func = Box::new(|call_ctx: &mut MultiUseGuestCallContext| { - call_ctx.call::("AddToStatic", 5i32)?; - Ok(()) - }); - let transition_func = MultiUseContextCallback::from(func); - let mut sbox2 = sbox1.evolve(transition_func).unwrap(); - let res: i32 = sbox2.call_guest_function_by_name("GetStatic", ()).unwrap(); + let snapshot = sbox.snapshot().unwrap(); + + sbox.call_guest_function_by_name::("AddToStatic", 5i32) + .unwrap(); + + let res: i32 = sbox.call_guest_function_by_name("GetStatic", ()).unwrap(); assert_eq!(res, 5); - let mut sbox3: MultiUseSandbox = sbox2.devolve(Noop::default()).unwrap(); - let res: i32 = sbox3.call_guest_function_by_name("GetStatic", ()).unwrap(); + + sbox.restore(&snapshot).unwrap(); + let res: i32 = sbox.call_guest_function_by_name("GetStatic", ()).unwrap(); assert_eq!(res, 0); } @@ -494,7 +383,7 @@ mod tests { usbox.register("MakeGetpidSyscall", make_get_pid_syscall)?; - let mut sbox: MultiUseSandbox = usbox.evolve(Noop::default())?; + let mut sbox: MultiUseSandbox = usbox.evolve()?; let res: Result = sbox.call_guest_function_by_name("ViolateSeccompFilters", ()); @@ -530,7 +419,7 @@ mod tests { )?; // ^^^ note, we are allowing SYS_getpid - let mut sbox: MultiUseSandbox = usbox.evolve(Noop::default())?; + let mut sbox: MultiUseSandbox = usbox.evolve()?; let res: Result = sbox.call_guest_function_by_name("ViolateSeccompFilters", ()); @@ -584,7 +473,7 @@ mod tests { .unwrap(); ubox.register("Openat_Hostfunc", make_openat_syscall)?; - let mut sbox = ubox.evolve(Noop::default()).unwrap(); + let mut sbox = ubox.evolve().unwrap(); let host_func_result = sbox .call_guest_function_by_name::( "CallGivenParamlessHostFuncThatReturnsI64", @@ -614,7 +503,7 @@ mod tests { make_openat_syscall, [libc::SYS_openat], )?; - let mut sbox = ubox.evolve(Noop::default()).unwrap(); + let mut sbox = ubox.evolve().unwrap(); let host_func_result = sbox .call_guest_function_by_name::( "CallGivenParamlessHostFuncThatReturnsI64", @@ -637,7 +526,7 @@ mod tests { ) .unwrap(); - let mut multi_use_sandbox: MultiUseSandbox = usbox.evolve(Noop::default()).unwrap(); + let mut multi_use_sandbox: MultiUseSandbox = usbox.evolve().unwrap(); let res: Result<()> = multi_use_sandbox.call_guest_function_by_name("TriggerException", ()); @@ -677,8 +566,7 @@ mod tests { ) .unwrap(); - let mut multi_use_sandbox: MultiUseSandbox = - usbox.evolve(Noop::default()).unwrap(); + let mut multi_use_sandbox: MultiUseSandbox = usbox.evolve().unwrap(); let res: i32 = multi_use_sandbox .call_guest_function_by_name("GetStatic", ()) @@ -706,7 +594,7 @@ mod tests { None, ) .unwrap() - .evolve(Noop::default()) + .evolve() .unwrap(); let expected = b"hello world"; diff --git a/src/hyperlight_host/src/sandbox/mod.rs b/src/hyperlight_host/src/sandbox/mod.rs index c6e33c84b..07389c369 100644 --- a/src/hyperlight_host/src/sandbox/mod.rs +++ b/src/hyperlight_host/src/sandbox/mod.rs @@ -37,6 +37,9 @@ pub mod uninitialized; /// initialized `Sandbox`es. pub(crate) mod uninitialized_evolve; +/// Representation of a snapshot of a `Sandbox`. +pub mod snapshot; + /// Trait used by the macros to paper over the differences between hyperlight and hyperlight-wasm mod callable; @@ -101,8 +104,6 @@ mod tests { use hyperlight_testing::simple_guest_as_string; use crate::sandbox::uninitialized::GuestBinary; - use crate::sandbox_state::sandbox::EvolvableSandbox; - use crate::sandbox_state::transition::Noop; use crate::{MultiUseSandbox, UninitializedSandbox, new_error}; #[test] @@ -163,11 +164,9 @@ mod tests { )) .unwrap(); - let sandbox = uninitialized_sandbox - .evolve(Noop::default()) - .unwrap_or_else(|_| { - panic!("Failed to initialize UninitializedSandbox thread {}", i) - }); + let sandbox = uninitialized_sandbox.evolve().unwrap_or_else(|_| { + panic!("Failed to initialize UninitializedSandbox thread {}", i) + }); sq.push(sandbox).unwrap_or_else(|_| { panic!("Failed to push UninitializedSandbox thread {}", i) diff --git a/src/hyperlight_host/src/sandbox_state/mod.rs b/src/hyperlight_host/src/sandbox/snapshot.rs similarity index 63% rename from src/hyperlight_host/src/sandbox_state/mod.rs rename to src/hyperlight_host/src/sandbox/snapshot.rs index 474cad72a..d91f52437 100644 --- a/src/hyperlight_host/src/sandbox_state/mod.rs +++ b/src/hyperlight_host/src/sandbox/snapshot.rs @@ -1,5 +1,5 @@ /* -Copyright 2025 The Hyperlight Authors. +Copyright 2025 The Hyperlight Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,8 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -/// The standardized `Sandbox` trait and the ways it ban be transitioned -/// to a different `Sandbox` trait -pub mod sandbox; -/// Metadata about transitions between `Sandbox` states -pub mod transition; +use crate::mem::shared_mem_snapshot::SharedMemorySnapshot; + +/// A snapshot capturing the state of the memory in a `MultiUseSandbox`. +#[derive(Clone)] +pub struct Snapshot { + /// TODO: Use Arc + pub(crate) inner: SharedMemorySnapshot, +} diff --git a/src/hyperlight_host/src/sandbox/uninitialized.rs b/src/hyperlight_host/src/sandbox/uninitialized.rs index e27f91ff2..f5149f5cc 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized.rs @@ -34,9 +34,7 @@ use crate::mem::memory_region::{DEFAULT_GUEST_BLOB_MEM_FLAGS, MemoryRegionFlags} use crate::mem::mgr::{STACK_COOKIE_LEN, SandboxMemoryManager}; use crate::mem::shared_mem::ExclusiveSharedMemory; use crate::sandbox::SandboxConfiguration; -use crate::sandbox_state::sandbox::EvolvableSandbox; -use crate::sandbox_state::transition::Noop; -use crate::{MultiUseSandbox, Result, log_then_return, new_error}; +use crate::{MultiUseSandbox, Result, new_error}; #[cfg(all(target_os = "linux", feature = "seccomp"))] const EXTRA_ALLOWED_SYSCALLS_FOR_WRITER_FUNC: &[super::ExtraAllowedSyscall] = &[ @@ -82,18 +80,6 @@ pub struct UninitializedSandbox { pub(crate) rt_cfg: SandboxRuntimeConfig, } -impl crate::sandbox_state::sandbox::UninitializedSandbox for UninitializedSandbox { - #[instrument(skip_all, parent = Span::current(), level = "Trace")] - fn get_uninitialized_sandbox(&self) -> &crate::sandbox::UninitializedSandbox { - self - } - - #[instrument(skip_all, parent = Span::current(), level = "Trace")] - fn get_uninitialized_sandbox_mut(&mut self) -> &mut crate::sandbox::UninitializedSandbox { - self - } -} - impl Debug for UninitializedSandbox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("UninitializedSandbox") @@ -102,24 +88,10 @@ impl Debug for UninitializedSandbox { } } -impl crate::sandbox_state::sandbox::Sandbox for UninitializedSandbox { - fn check_stack_guard(&self) -> Result { - log_then_return!( - "Checking the stack cookie before the sandbox is initialized is unsupported" - ); - } -} - -impl - EvolvableSandbox< - UninitializedSandbox, - MultiUseSandbox, - Noop, - > for UninitializedSandbox -{ +impl UninitializedSandbox { /// Evolve `self` to a `MultiUseSandbox` without any additional metadata. #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] - fn evolve(self, _: Noop) -> Result { + pub fn evolve(self) -> Result { evolve_impl_multi_use(self) } } @@ -433,8 +405,6 @@ mod tests { use crate::sandbox::SandboxConfiguration; use crate::sandbox::uninitialized::{GuestBinary, GuestEnvironment}; - use crate::sandbox_state::sandbox::EvolvableSandbox; - use crate::sandbox_state::transition::Noop; use crate::{MultiUseSandbox, Result, UninitializedSandbox, new_error}; #[test] @@ -445,7 +415,7 @@ mod tests { GuestEnvironment::new(GuestBinary::FilePath(binary_path.clone()), Some(&buffer)); let uninitialized_sandbox = UninitializedSandbox::new(guest_env, None).unwrap(); - let mut sandbox: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default()).unwrap(); + let mut sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap(); let res = sandbox .call_guest_function_by_name::>("ReadFromUserMemory", (4u64, buffer.to_vec())) @@ -489,7 +459,7 @@ mod tests { // Get a Sandbox from an uninitialized sandbox without a call back function - let _sandbox: MultiUseSandbox = uninitialized_sandbox.evolve(Noop::default()).unwrap(); + let _sandbox: MultiUseSandbox = uninitialized_sandbox.evolve().unwrap(); // Test with a valid guest binary buffer @@ -537,7 +507,7 @@ mod tests { usbox.register("test0", |arg: i32| Ok(arg + 1)).unwrap(); - let sandbox: Result = usbox.evolve(Noop::default()); + let sandbox: Result = usbox.evolve(); assert!(sandbox.is_ok()); let sandbox = sandbox.unwrap(); @@ -562,7 +532,7 @@ mod tests { usbox.register("test1", |a: i32, b: i32| Ok(a + b)).unwrap(); - let sandbox: Result = usbox.evolve(Noop::default()); + let sandbox: Result = usbox.evolve(); assert!(sandbox.is_ok()); let sandbox = sandbox.unwrap(); @@ -595,7 +565,7 @@ mod tests { }) .unwrap(); - let sandbox: Result = usbox.evolve(Noop::default()); + let sandbox: Result = usbox.evolve(); assert!(sandbox.is_ok()); let sandbox = sandbox.unwrap(); @@ -613,7 +583,7 @@ mod tests { // calling a function that doesn't exist { let usbox = uninitialized_sandbox(); - let sandbox: Result = usbox.evolve(Noop::default()); + let sandbox: Result = usbox.evolve(); assert!(sandbox.is_ok()); let sandbox = sandbox.unwrap(); @@ -836,11 +806,9 @@ mod tests { .host_print(format!("Print from UninitializedSandbox on Thread {}\n", i)) .unwrap(); - let sandbox = uninitialized_sandbox - .evolve(Noop::default()) - .unwrap_or_else(|_| { - panic!("Failed to initialize UninitializedSandbox thread {}", i) - }); + let sandbox = uninitialized_sandbox.evolve().unwrap_or_else(|_| { + panic!("Failed to initialize UninitializedSandbox thread {}", i) + }); sq.push(sandbox).unwrap_or_else(|_| { panic!("Failed to push UninitializedSandbox thread {}", i) @@ -1128,7 +1096,7 @@ mod tests { ); res.unwrap() }; - let _: Result = sbox.evolve(Noop::default()); + let _: Result = sbox.evolve(); let num_calls = TEST_LOGGER.num_log_calls(); diff --git a/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs b/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs index a37f747e2..95732ad8f 100644 --- a/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs +++ b/src/hyperlight_host/src/sandbox/uninitialized_evolve.rs @@ -41,7 +41,6 @@ use crate::sandbox::host_funcs::FunctionRegistry; use crate::sandbox::mem_access::mem_access_handler_wrapper; use crate::sandbox::outb::outb_handler_wrapper; use crate::sandbox::{HostSharedMemory, MemMgrWrapper}; -use crate::sandbox_state::sandbox::Sandbox; #[cfg(target_os = "linux")] use crate::signal_handlers::setup_signal_handlers; use crate::{MultiUseSandbox, Result, UninitializedSandbox, log_then_return, new_error}; @@ -58,7 +57,7 @@ use crate::{MultiUseSandbox, Result, UninitializedSandbox, log_then_return, new_ /// If this doesn't make sense, and you want to change this type, /// please reach out to a Hyperlight developer before making the change. #[instrument(err(Debug), skip_all, , parent = Span::current(), level = "Trace")] -fn evolve_impl( +fn evolve_impl( u_sbox: UninitializedSandbox, transform: TransformFunc, ) -> Result @@ -127,24 +126,20 @@ where #[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")] pub(super) fn evolve_impl_multi_use(u_sbox: UninitializedSandbox) -> Result { - evolve_impl( - u_sbox, - |hf, mut hshm, vm, out_hdl, mem_hdl, dispatch_ptr| { - { - hshm.as_mut().push_state()?; - } - Ok(MultiUseSandbox::from_uninit( - hf, - hshm.clone(), - vm, - out_hdl, - mem_hdl, - dispatch_ptr, - #[cfg(gdb)] - dbg_mem_access_handler_wrapper(hshm), - )) - }, - ) + evolve_impl(u_sbox, |hf, hshm, vm, out_hdl, mem_hdl, dispatch_ptr| { + #[cfg(gdb)] + let dbg_mem_wrapper = dbg_mem_access_handler_wrapper(hshm.clone()); + Ok(MultiUseSandbox::from_uninit( + hf, + hshm, + vm, + out_hdl, + mem_hdl, + dispatch_ptr, + #[cfg(gdb)] + dbg_mem_wrapper, + )) + }) } pub(crate) fn set_up_hypervisor_partition( diff --git a/src/hyperlight_host/src/sandbox_state/sandbox.rs b/src/hyperlight_host/src/sandbox_state/sandbox.rs deleted file mode 100644 index 4f9641af8..000000000 --- a/src/hyperlight_host/src/sandbox_state/sandbox.rs +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright 2025 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use std::fmt::Debug; - -use tracing::{Span, instrument}; - -use super::transition::TransitionMetadata; -use crate::{Result, new_error}; - -/// The minimal functionality of a Hyperlight sandbox. Most of the types -/// and operations within this crate require `Sandbox` implementations. -/// -/// `Sandbox`es include the notion of an ordering in a state machine. -/// For example, a given `Sandbox` implementation may be the root node -/// in the state machine to which it belongs, and it may know how to "evolve" -/// into a next state. That "next state" may in turn know how to roll back -/// to the root node. -/// -/// These transitions are expressed as `EvolvableSandbox` and -/// `DevolvableSandbox` implementations any `Sandbox` implementation can -/// opt into. -pub trait Sandbox: Sized + Debug { - /// Check to ensure the current stack cookie matches the one that - /// was selected when the stack was constructed. - /// - /// Return an `Err` if there was an error inspecting the stack, `Ok(false)` - /// if there was no such error but the stack guard doesn't match, and - /// `Ok(true)` in the same situation where the stack guard does match. - /// - - // NOTE: this is only needed for UninitializedSandbox and MultiUseSandbox - // Those are the only types that need implement this trait - // The default implementation is provided so that types that implement Sandbox (e.g. JSSandbox) but do not need to implement this trait do not need to provide an implementation - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn check_stack_guard(&self) -> Result { - Err(new_error!( - "check_stack_guard not implemented for this type" - )) - } -} - -/// A utility trait to recognize a Sandbox that has not yet been initialized. -/// It allows retrieval of a strongly typed UninitializedSandbox. -pub trait UninitializedSandbox: Sandbox { - /// Retrieves reference to strongly typed `UninitializedSandbox` - fn get_uninitialized_sandbox(&self) -> &crate::sandbox::UninitializedSandbox; - - /// Retrieves mutable reference to strongly typed `UninitializedSandbox` - fn get_uninitialized_sandbox_mut(&mut self) -> &mut crate::sandbox::UninitializedSandbox; -} - -/// A `Sandbox` that knows how to "evolve" into a next state. -pub trait EvolvableSandbox>: - Sandbox -{ - /// Evolve `Self` to `Next` providing Metadata. - fn evolve(self, tsn: T) -> Result; -} - -/// A `Sandbox` that knows how to roll back to a "previous" `Sandbox` -pub trait DevolvableSandbox>: - Sandbox -{ - /// Devolve `Self` to `Prev` providing Metadata. - fn devolve(self, tsn: T) -> Result; -} diff --git a/src/hyperlight_host/src/sandbox_state/transition.rs b/src/hyperlight_host/src/sandbox_state/transition.rs deleted file mode 100644 index 3cd7aaadf..000000000 --- a/src/hyperlight_host/src/sandbox_state/transition.rs +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright 2025 The Hyperlight Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -use std::marker::PhantomData; - -use tracing::{Span, instrument}; - -use super::sandbox::Sandbox; -use crate::Result; -use crate::func::call_ctx::MultiUseGuestCallContext; - -/// Metadata about an evolution or devolution. Any `Sandbox` implementation -/// that also implements `EvolvableSandbox` or `DevolvableSandbox` -/// can decide the following things in a type-safe way: -/// -/// 1. That transition is possible -/// 2. That transition requires a specific kind of metadata -/// -/// For example, if you have the following structs: -/// -/// ```ignore -/// struct MySandbox1 {} -/// struct MySandbox2 {} -/// -/// impl Sandbox for MySandbox1 {...} -/// impl Sandbox for MySandbox2 {...} -/// ``` -/// -/// ...then you can define a metadata-free evolve transition between -/// `MySandbox1` and `MySandbox2`, and a devolve transition that requires -/// a callback between `MySandbox2` and `MySandbox` as follows: -/// -/// ```ignore -/// impl EvolvableSandbox< -/// MySandbox1, -/// MySandbox2, -/// Noop -/// > for MySandbox1 { -/// fn evolve( -/// self, -/// _: Noop -/// ) -> Result { -/// Ok(MySandbox2{}) -/// } -/// } -/// -/// ``` -/// -/// Most transitions will likely involve `Noop`, but some may involve -/// implementing their own. -pub trait TransitionMetadata {} - -/// Transition metadata that contains and does nothing. `Noop` is a -/// placeholder when you want to implement an `EvolvableSandbox` -/// or `DevolvableSandbox` that needs no additional metadata to succeed. -/// -/// Construct one of these by using the `default()` method. -pub struct Noop { - cur_ph: PhantomData, - next_ph: PhantomData, -} - -impl Default for Noop { - fn default() -> Self { - Self { - cur_ph: PhantomData, - next_ph: PhantomData, - } - } -} - -impl TransitionMetadata for Noop {} - -/// A `TransitionMetadata` that calls a callback. The callback function takes -/// a mutable reference to a `MultiUseGuestCallContext` and returns a `Result<()>` -/// to signify success or failure of the function. -/// -/// The function use the context to call guest functions. -/// -/// Construct one of these by passing your callback to -/// `MultiUseContextCallback::from`, as in the following code (assuming `MySandbox` -/// is a `Sandbox` implementation): -/// -/// ```ignore -/// let my_cb_fn: dyn FnOnce(&mut MultiUseGuestCallContext) -> Result<()> = |_sbox| { -/// println!("hello world!"); -/// }; -/// let mutating_cb = MultiUseContextCallback::from(my_cb_fn); -/// ``` -pub struct MultiUseContextCallback<'func, Cur: Sandbox, F> -where - F: FnOnce(&mut MultiUseGuestCallContext) -> Result<()> + 'func, -{ - cur_ph: PhantomData, - fn_life_ph: PhantomData<&'func ()>, - cb: F, -} - -impl TransitionMetadata - for MultiUseContextCallback<'_, Cur, F> -where - F: FnOnce(&mut MultiUseGuestCallContext) -> Result<()>, -{ -} - -impl MultiUseContextCallback<'_, Cur, F> -where - F: FnOnce(&mut MultiUseGuestCallContext) -> Result<()>, -{ - /// Invokes the callback on the provided guest context - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - pub fn call(self, cur: &mut MultiUseGuestCallContext) -> Result<()> { - (self.cb)(cur) - } -} - -impl<'a, Cur: Sandbox, F> From for MultiUseContextCallback<'a, Cur, F> -where - F: FnOnce(&mut MultiUseGuestCallContext) -> Result<()> + 'a, -{ - #[instrument(skip_all, parent = Span::current(), level= "Trace")] - fn from(val: F) -> Self { - MultiUseContextCallback { - cur_ph: PhantomData, - fn_life_ph: PhantomData, - cb: val, - } - } -} -#[cfg(test)] -mod tests { - use super::Noop; - use crate::Result; - use crate::sandbox_state::sandbox::{DevolvableSandbox, EvolvableSandbox, Sandbox}; - - #[derive(Debug, Eq, PartialEq, Clone)] - struct MySandbox1 {} - #[derive(Debug, Eq, PartialEq, Clone)] - struct MySandbox2 {} - - impl Sandbox for MySandbox1 {} - impl Sandbox for MySandbox2 {} - - impl EvolvableSandbox> for MySandbox1 { - fn evolve(self, _: Noop) -> Result { - Ok(MySandbox2 {}) - } - } - - impl DevolvableSandbox> for MySandbox2 { - fn devolve(self, _: Noop) -> Result { - Ok(MySandbox1 {}) - } - } - - #[test] - fn test_evolve_devolve() { - let sbox_1_1 = MySandbox1 {}; - let sbox_2_1 = sbox_1_1.clone().evolve(Noop::default()).unwrap(); - let sbox_1_2 = sbox_2_1.clone().devolve(Noop::default()).unwrap(); - let sbox_2_2 = sbox_1_2.clone().evolve(Noop::default()).unwrap(); - assert_eq!(sbox_1_1, sbox_1_2); - assert_eq!(sbox_2_1, sbox_2_2); - } -} diff --git a/src/hyperlight_host/tests/common/mod.rs b/src/hyperlight_host/tests/common/mod.rs index b3d465bf3..5ff8cc3e0 100644 --- a/src/hyperlight_host/tests/common/mod.rs +++ b/src/hyperlight_host/tests/common/mod.rs @@ -14,8 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ use hyperlight_host::func::HostFunction; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; +use hyperlight_host::sandbox::SandboxConfiguration; use hyperlight_host::{GuestBinary, MultiUseSandbox, Result, UninitializedSandbox}; use hyperlight_testing::{ c_callback_guest_as_string, c_simple_guest_as_string, callback_guest_as_string, @@ -34,9 +33,12 @@ pub fn new_uninit() -> Result { /// Use this instead of the `new_uninit` if you want your test to only run with the rust guest, not the c guest pub fn new_uninit_rust() -> Result { + let mut cfg = SandboxConfiguration::default(); + cfg.set_input_data_size(112 * 500 + 7); // 1 MiB + //cfg.set_output_data_size(500 * 100); // 1 MiB UninitializedSandbox::new( GuestBinary::FilePath(simple_guest_as_string().unwrap()), - None, + Some(cfg), ) } @@ -56,7 +58,7 @@ pub fn get_simpleguest_sandboxes( if let Some(writer) = writer.clone() { sandbox.register_print(writer).unwrap(); } - sandbox.evolve(Noop::default()).unwrap() + sandbox.evolve().unwrap() }) .collect() } diff --git a/src/hyperlight_host/tests/integration_test.rs b/src/hyperlight_host/tests/integration_test.rs index 2db32b4a0..967d93c53 100644 --- a/src/hyperlight_host/tests/integration_test.rs +++ b/src/hyperlight_host/tests/integration_test.rs @@ -22,8 +22,6 @@ use std::time::Duration; use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode; use hyperlight_common::mem::PAGE_SIZE; use hyperlight_host::sandbox::SandboxConfiguration; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{GuestBinary, HyperlightError, MultiUseSandbox, UninitializedSandbox}; use hyperlight_testing::simplelogger::{LOGGER, SimpleLogger}; use hyperlight_testing::{ @@ -61,7 +59,7 @@ fn interrupt_host_call() { .register_with_extra_allowed_syscalls("Spin", spin, vec![libc::SYS_clock_nanosleep]) .unwrap(); - let mut sandbox: MultiUseSandbox = usbox.evolve(Noop::default()).unwrap(); + let mut sandbox: MultiUseSandbox = usbox.evolve().unwrap(); let interrupt_handle = sandbox.interrupt_handle(); assert!(!interrupt_handle.dropped()); // not yet dropped @@ -83,7 +81,7 @@ fn interrupt_host_call() { /// Makes sure a running guest call can be interrupted by the host #[test] fn interrupt_in_progress_guest_call() { - let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); let barrier = Arc::new(Barrier::new(2)); let barrier2 = barrier.clone(); let interrupt_handle = sbox1.interrupt_handle(); @@ -118,7 +116,7 @@ fn interrupt_in_progress_guest_call() { /// Makes sure interrupting a vm before the guest call has started also prevents the guest call from being executed #[test] fn interrupt_guest_call_in_advance() { - let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); let barrier = Arc::new(Barrier::new(2)); let barrier2 = barrier.clone(); let interrupt_handle = sbox1.interrupt_handle(); @@ -160,9 +158,9 @@ fn interrupt_guest_call_in_advance() { /// all possible interleavings, but can hopefully increases confidence somewhat. #[test] fn interrupt_same_thread() { - let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); - let mut sbox2: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); - let mut sbox3: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); + let mut sbox2: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); + let mut sbox3: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); let barrier = Arc::new(Barrier::new(2)); let barrier2 = barrier.clone(); @@ -202,9 +200,9 @@ fn interrupt_same_thread() { /// Same test as above but with no per-iteration barrier, to get more possible interleavings. #[test] fn interrupt_same_thread_no_barrier() { - let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); - let mut sbox2: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); - let mut sbox3: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); + let mut sbox2: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); + let mut sbox3: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); let barrier = Arc::new(Barrier::new(2)); let barrier2 = barrier.clone(); @@ -248,8 +246,8 @@ fn interrupt_same_thread_no_barrier() { // and that anther sandbox on the original thread does not get incorrectly killed #[test] fn interrupt_moved_sandbox() { - let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); - let mut sbox2: MultiUseSandbox = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); + let mut sbox2: MultiUseSandbox = new_uninit_rust().unwrap().evolve().unwrap(); let interrupt_handle = sbox1.interrupt_handle(); let interrupt_handle2 = sbox2.interrupt_handle(); @@ -300,7 +298,7 @@ fn interrupt_custom_signal_no_and_retry_delay() { Some(config), ) .unwrap() - .evolve(Noop::default()) + .evolve() .unwrap(); let interrupt_handle = sbox1.interrupt_handle(); @@ -340,7 +338,7 @@ fn interrupt_spamming_host_call() { // do nothing }) .unwrap(); - let mut sbox1: MultiUseSandbox = uninit.evolve(Noop::default()).unwrap(); + let mut sbox1: MultiUseSandbox = uninit.evolve().unwrap(); let interrupt_handle = sbox1.interrupt_handle(); @@ -369,7 +367,7 @@ fn print_four_args_c_guest() { let path = c_simple_guest_as_string().unwrap(); let guest_path = GuestBinary::FilePath(path); let uninit = UninitializedSandbox::new(guest_path, None); - let mut sbox1 = uninit.unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = uninit.unwrap().evolve().unwrap(); let res = sbox1.call_guest_function_by_name::( "PrintFourArgs", @@ -382,7 +380,7 @@ fn print_four_args_c_guest() { // Checks that guest can abort with a specific code. #[test] fn guest_abort() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let error_code: u8 = 13; // this is arbitrary let res = sbox1 .call_guest_function_by_name::<()>("GuestAbortWithCode", error_code as i32) @@ -395,7 +393,7 @@ fn guest_abort() { #[test] fn guest_abort_with_context1() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let res = sbox1 .call_guest_function_by_name::<()>("GuestAbortWithMessage", (25_i32, "Oh no".to_string())) @@ -408,7 +406,7 @@ fn guest_abort_with_context1() { #[test] fn guest_abort_with_context2() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); // The buffer size for the panic context is 1024 bytes. // This test will see what happens if the panic message is longer than that @@ -462,7 +460,7 @@ fn guest_abort_c_guest() { let path = c_simple_guest_as_string().unwrap(); let guest_path = GuestBinary::FilePath(path); let uninit = UninitializedSandbox::new(guest_path, None); - let mut sbox1 = uninit.unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = uninit.unwrap().evolve().unwrap(); let res = sbox1 .call_guest_function_by_name::<()>( @@ -479,7 +477,7 @@ fn guest_abort_c_guest() { #[test] fn guest_panic() { // this test is rust-specific - let mut sbox1 = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit_rust().unwrap().evolve().unwrap(); let res = sbox1 .call_guest_function_by_name::<()>("guest_panic", "Error... error...".to_string()) @@ -493,7 +491,7 @@ fn guest_panic() { #[test] fn guest_malloc() { // this test is rust-only - let mut sbox1 = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit_rust().unwrap().evolve().unwrap(); let size_to_allocate = 2000_i32; sbox1 @@ -503,7 +501,7 @@ fn guest_malloc() { #[test] fn guest_allocate_vec() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let size_to_allocate = 2000_i32; @@ -520,7 +518,7 @@ fn guest_allocate_vec() { // checks that malloc failures are captured correctly #[test] fn guest_malloc_abort() { - let mut sbox1 = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit_rust().unwrap().evolve().unwrap(); let size = 20000000_i32; // some big number that should fail when allocated @@ -544,7 +542,7 @@ fn guest_malloc_abort() { Some(cfg), ) .unwrap(); - let mut sbox2 = uninit.evolve(Noop::default()).unwrap(); + let mut sbox2 = uninit.evolve().unwrap(); let res = sbox2.call_guest_function_by_name::( "CallMalloc", // uses the rust allocator to allocate a vector on heap @@ -564,7 +562,7 @@ fn dynamic_stack_allocate_c_guest() { let path = c_simple_guest_as_string().unwrap(); let guest_path = GuestBinary::FilePath(path); let uninit = UninitializedSandbox::new(guest_path, None); - let mut sbox1: MultiUseSandbox = uninit.unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1: MultiUseSandbox = uninit.unwrap().evolve().unwrap(); let res: i32 = sbox1 .call_guest_function_by_name("StackAllocate", 100_i32) @@ -580,7 +578,7 @@ fn dynamic_stack_allocate_c_guest() { // checks that a small buffer on stack works #[test] fn static_stack_allocate() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let res: i32 = sbox1.call_guest_function_by_name("SmallVar", ()).unwrap(); assert_eq!(res, 1024); @@ -589,7 +587,7 @@ fn static_stack_allocate() { // checks that a huge buffer on stack fails with stackoverflow #[test] fn static_stack_allocate_overflow() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let res = sbox1 .call_guest_function_by_name::("LargeVar", ()) .unwrap_err(); @@ -599,7 +597,7 @@ fn static_stack_allocate_overflow() { // checks that a recursive function with stack allocation works, (that chkstk can be called without overflowing) #[test] fn recursive_stack_allocate() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let iterations = 1_i32; @@ -629,7 +627,7 @@ fn guard_page_check() { for offset in offsets_from_page_guard_start { // we have to create a sandbox each iteration because can't reuse after MMIO error in release mode - let mut sbox1 = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit_rust().unwrap().evolve().unwrap(); let result = sbox1.call_guest_function_by_name::("test_write_raw_ptr", offset); if guard_range.contains(&offset) { // should have failed @@ -646,7 +644,7 @@ fn guard_page_check() { #[test] fn guard_page_check_2() { // this test is rust-guest only - let mut sbox1 = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit_rust().unwrap().evolve().unwrap(); let result = sbox1 .call_guest_function_by_name::<()>("InfiniteRecursion", ()) @@ -656,7 +654,7 @@ fn guard_page_check_2() { #[test] fn execute_on_stack() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let result = sbox1 .call_guest_function_by_name::("ExecuteOnStack", ()) @@ -672,7 +670,7 @@ fn execute_on_stack() { #[test] #[ignore] // ran from Justfile because requires feature "executable_heap" fn execute_on_heap() { - let mut sbox1 = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit_rust().unwrap().evolve().unwrap(); let result = sbox1.call_guest_function_by_name::("ExecuteOnHeap", ()); println!("{:#?}", result); @@ -688,22 +686,10 @@ fn execute_on_heap() { } } -#[test] -fn memory_resets_after_failed_guestcall() { - let mut sbox1 = new_uninit_rust().unwrap().evolve(Noop::default()).unwrap(); - sbox1 - .call_guest_function_by_name::("AddToStaticAndFail", ()) - .unwrap_err(); - let res = sbox1 - .call_guest_function_by_name::("GetStatic", ()) - .unwrap(); - assert_eq!(res, 0, "Expected 0, got {:?}", res); -} - // checks that a recursive function with stack allocation eventually fails with stackoverflow #[test] fn recursive_stack_allocate_overflow() { - let mut sbox1 = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sbox1 = new_uninit().unwrap().evolve().unwrap(); let iterations = 10_i32; @@ -778,7 +764,7 @@ fn log_test_messages(levelfilter: Option) { sbox.set_max_guest_log_level(levelfilter); } - let mut sbox1 = sbox.evolve(Noop::default()).unwrap(); + let mut sbox1 = sbox.evolve().unwrap(); let message = format!("Hello from log_message level {}", level as i32); sbox1 diff --git a/src/hyperlight_host/tests/sandbox_host_tests.rs b/src/hyperlight_host/tests/sandbox_host_tests.rs index 1c8bf92b6..7ca0ba5dc 100644 --- a/src/hyperlight_host/tests/sandbox_host_tests.rs +++ b/src/hyperlight_host/tests/sandbox_host_tests.rs @@ -20,8 +20,6 @@ use std::sync::{Arc, Mutex}; use common::new_uninit; use hyperlight_host::sandbox::{Callable, SandboxConfiguration}; -use hyperlight_host::sandbox_state::sandbox::EvolvableSandbox; -use hyperlight_host::sandbox_state::transition::Noop; use hyperlight_host::{ GuestBinary, HyperlightError, MultiUseSandbox, Result, UninitializedSandbox, new_error, }; @@ -35,16 +33,16 @@ use crate::common::{get_callbackguest_uninit_sandboxes, get_simpleguest_sandboxe #[test] #[cfg_attr(target_os = "windows", serial)] // using LoadLibrary requires serial tests fn pass_byte_array() { - for sandbox in get_simpleguest_sandboxes(None).into_iter() { - let mut ctx = sandbox.new_call_context(); + for mut sandbox in get_simpleguest_sandboxes(None).into_iter() { const LEN: usize = 10; let bytes = vec![1u8; LEN]; - let res: Vec = ctx + let res: Vec = sandbox .call("SetByteArrayToZero", bytes.clone()) .expect("Expected VecBytes"); assert_eq!(res, [0; LEN]); - ctx.call::("SetByteArrayToZeroNoLength", bytes.clone()) + sandbox + .call::("SetByteArrayToZeroNoLength", bytes.clone()) .unwrap_err(); // missing length param } } @@ -85,7 +83,7 @@ fn float_roundtrip() { f32::NAN, -f32::NAN, ]; - let mut sandbox: MultiUseSandbox = new_uninit().unwrap().evolve(Noop::default()).unwrap(); + let mut sandbox: MultiUseSandbox = new_uninit().unwrap().evolve().unwrap(); for f in doubles.iter() { let res: f64 = sandbox .call_guest_function_by_name("EchoDouble", *f) @@ -332,7 +330,7 @@ fn callback_test_helper() -> Result<()> { })?; // call guest function that calls host function - let mut init_sandbox: MultiUseSandbox = sandbox.evolve(Noop::default())?; + let mut init_sandbox: MultiUseSandbox = sandbox.evolve()?; let msg = "Hello world"; init_sandbox.call_guest_function_by_name::("GuestMethod1", msg.to_string())?; @@ -374,7 +372,7 @@ fn host_function_error() -> Result<()> { })?; // call guest function that calls host function - let mut init_sandbox: MultiUseSandbox = sandbox.evolve(Noop::default())?; + let mut init_sandbox: MultiUseSandbox = sandbox.evolve()?; let msg = "Hello world"; let res = init_sandbox .call_guest_function_by_name::("GuestMethod1", msg.to_string()) diff --git a/src/hyperlight_host/tests/wit_test.rs b/src/hyperlight_host/tests/wit_test.rs index e42441dd8..158945148 100644 --- a/src/hyperlight_host/tests/wit_test.rs +++ b/src/hyperlight_host/tests/wit_test.rs @@ -18,7 +18,7 @@ limitations under the License. use std::sync::{Arc, Mutex}; use hyperlight_common::resource::BorrowedResourceGuard; -use hyperlight_host::{GuestBinary, MultiUseGuestCallContext, UninitializedSandbox}; +use hyperlight_host::{GuestBinary, MultiUseSandbox, UninitializedSandbox}; use hyperlight_testing::wit_guest_as_string; extern crate alloc; @@ -238,7 +238,7 @@ impl test::wit::TestImports for Host { } } -fn sb() -> TestSandbox { +fn sb() -> TestSandbox { let path = wit_guest_as_string().unwrap(); let guest_path = GuestBinary::FilePath(path); let uninit = UninitializedSandbox::new(guest_path, None).unwrap();