Skip to content

Commit a4ae685

Browse files
committed
Test cross-guest snapshot restore
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent 0bd15f1 commit a4ae685

2 files changed

Lines changed: 361 additions & 6 deletions

File tree

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 201 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,14 +1254,15 @@ mod tests {
12541254

12551255
use hyperlight_common::flatbuffer_wrappers::guest_error::ErrorCode;
12561256
use hyperlight_testing::sandbox_sizes::{LARGE_HEAP_SIZE, MEDIUM_HEAP_SIZE, SMALL_HEAP_SIZE};
1257-
use hyperlight_testing::simple_guest_as_pathbuf;
1257+
use hyperlight_testing::{c_simple_guest_as_pathbuf, simple_guest_as_pathbuf};
12581258

12591259
#[cfg(not(gdb))]
12601260
use crate::hypervisor::hyperlight_vm::{HyperlightVmError, test_support::VmOperation};
12611261
use crate::func::host_functions::Registerable;
12621262
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags, MemoryRegionType};
12631263
use crate::mem::shared_mem::{ExclusiveSharedMemory, GuestSharedMemory, SharedMemory as _};
12641264
use crate::sandbox::SandboxConfiguration;
1265+
use crate::sandbox::uninitialized::{GuestBlob, GuestEnvironment};
12651266
use crate::{
12661267
GuestBinary, HyperlightError, MultiUseSandbox, Result, SandboxStatus, UninitializedSandbox,
12671268
};
@@ -2485,7 +2486,7 @@ mod tests {
24852486
target.restore(snapshot).unwrap();
24862487
assert_eq!(target.mem_mgr.layout.heap_size(), 0x6000);
24872488
assert!(target.call::<i32>("CallMalloc", 0x10_000i32).is_err());
2488-
assert!(target.poisoned());
2489+
assert!(target.status().is_poisoned());
24892490
}
24902491

24912492
#[test]
@@ -2515,7 +2516,7 @@ mod tests {
25152516
assert_eq!(target.mem_mgr.layout.input_data_size(), 0x2000);
25162517
assert_eq!(target.mem_mgr.layout.output_data_size(), 0x2000);
25172518
assert!(target.call::<String>("Echo", large).is_err());
2518-
assert!(!target.poisoned());
2519+
assert!(!target.status().is_poisoned());
25192520
assert_eq!(
25202521
target.call::<String>("Echo", "small".to_string()).unwrap(),
25212522
"small"
@@ -2575,6 +2576,203 @@ mod tests {
25752576
assert_eq!(target.mem_mgr.layout.heap_size(), 0x6000);
25762577
}
25772578

2579+
#[test]
2580+
fn snapshot_restore_replaces_rust_guest_with_c_guest() {
2581+
let init_data = b"cross-layout-init-data";
2582+
let source_env = GuestEnvironment {
2583+
guest_binary: GuestBinary::FilePath(c_simple_guest_as_pathbuf()),
2584+
init_data: Some(GuestBlob {
2585+
data: init_data,
2586+
permissions: MemoryRegionFlags::READ | MemoryRegionFlags::WRITE,
2587+
}),
2588+
};
2589+
let mut source = UninitializedSandbox::new(source_env, None)
2590+
.unwrap()
2591+
.evolve()
2592+
.unwrap();
2593+
let mut target = UninitializedSandbox::new(
2594+
GuestBinary::FilePath(simple_guest_as_pathbuf()),
2595+
None,
2596+
)
2597+
.unwrap()
2598+
.evolve()
2599+
.unwrap();
2600+
2601+
assert_eq!(source.call::<i32>("StackAllocate", 256i32).unwrap(), 256);
2602+
assert_eq!(target.call::<i32>("AddToStatic", 17i32).unwrap(), 17);
2603+
target.set_pt_root_finder(Box::new(|_, _, root| vec![root]));
2604+
assert!(target.pt_root_finder.is_some());
2605+
2606+
assert_ne!(
2607+
source.mem_mgr.layout.code_size(),
2608+
target.mem_mgr.layout.code_size()
2609+
);
2610+
assert_ne!(
2611+
source.mem_mgr.layout.init_data_size(),
2612+
target.mem_mgr.layout.init_data_size()
2613+
);
2614+
assert_ne!(
2615+
source.mem_mgr.layout.init_data_permissions(),
2616+
target.mem_mgr.layout.init_data_permissions()
2617+
);
2618+
2619+
let snapshot = source.snapshot().unwrap();
2620+
target.restore(snapshot).unwrap();
2621+
assert!(target.pt_root_finder.is_none());
2622+
assert_eq!(target.call::<i32>("StackAllocate", 512i32).unwrap(), 512);
2623+
assert!(matches!(
2624+
target.call::<i32>("GetStatic", ()),
2625+
Err(HyperlightError::GuestError(
2626+
ErrorCode::GuestFunctionNotFound,
2627+
name
2628+
)) if name == "GetStatic"
2629+
));
2630+
}
2631+
2632+
#[test]
2633+
fn snapshot_restore_replaces_c_guest_with_rust_guest() {
2634+
let mut source = UninitializedSandbox::new(
2635+
GuestBinary::FilePath(simple_guest_as_pathbuf()),
2636+
None,
2637+
)
2638+
.unwrap()
2639+
.evolve()
2640+
.unwrap();
2641+
assert_eq!(source.call::<i32>("AddToStatic", 42i32).unwrap(), 42);
2642+
let snapshot = source.snapshot().unwrap();
2643+
2644+
let mut target = UninitializedSandbox::new(
2645+
GuestBinary::FilePath(c_simple_guest_as_pathbuf()),
2646+
None,
2647+
)
2648+
.unwrap()
2649+
.evolve()
2650+
.unwrap();
2651+
assert_eq!(target.call::<i32>("StackAllocate", 256i32).unwrap(), 256);
2652+
2653+
target.restore(snapshot).unwrap();
2654+
assert_eq!(target.call::<i32>("GetStatic", ()).unwrap(), 42);
2655+
assert!(matches!(
2656+
target.call::<i32>("StackAllocate", 512i32),
2657+
Err(HyperlightError::GuestError(
2658+
ErrorCode::GuestFunctionNotFound,
2659+
name
2660+
)) if name == "StackAllocate"
2661+
));
2662+
}
2663+
2664+
#[test]
2665+
fn snapshot_restore_alternates_c_and_rust_guests() {
2666+
let mut c_source = UninitializedSandbox::new(
2667+
GuestBinary::FilePath(c_simple_guest_as_pathbuf()),
2668+
None,
2669+
)
2670+
.unwrap()
2671+
.evolve()
2672+
.unwrap();
2673+
assert_eq!(c_source.call::<i32>("StackAllocate", 256i32).unwrap(), 256);
2674+
let c_snapshot = c_source.snapshot().unwrap();
2675+
2676+
let mut rust_source = UninitializedSandbox::new(
2677+
GuestBinary::FilePath(simple_guest_as_pathbuf()),
2678+
None,
2679+
)
2680+
.unwrap()
2681+
.evolve()
2682+
.unwrap();
2683+
rust_source.call::<i32>("AddToStatic", 42i32).unwrap();
2684+
let rust_snapshot = rust_source.snapshot().unwrap();
2685+
2686+
let mut target = UninitializedSandbox::new(
2687+
GuestBinary::FilePath(c_simple_guest_as_pathbuf()),
2688+
None,
2689+
)
2690+
.unwrap()
2691+
.evolve()
2692+
.unwrap();
2693+
assert_eq!(target.call::<i32>("StackAllocate", 256i32).unwrap(), 256);
2694+
2695+
target.restore(rust_snapshot).unwrap();
2696+
assert_eq!(target.call::<i32>("GetStatic", ()).unwrap(), 42);
2697+
assert!(matches!(
2698+
target.call::<i32>("StackAllocate", 512i32),
2699+
Err(HyperlightError::GuestError(
2700+
ErrorCode::GuestFunctionNotFound,
2701+
name
2702+
)) if name == "StackAllocate"
2703+
));
2704+
2705+
target.restore(c_snapshot).unwrap();
2706+
assert_eq!(target.call::<i32>("StackAllocate", 512i32).unwrap(), 512);
2707+
assert!(matches!(
2708+
target.call::<i32>("GetStatic", ()),
2709+
Err(HyperlightError::GuestError(
2710+
ErrorCode::GuestFunctionNotFound,
2711+
name
2712+
)) if name == "GetStatic"
2713+
));
2714+
}
2715+
2716+
#[test]
2717+
fn snapshot_restore_keeps_target_host_function_implementation() {
2718+
let path = simple_guest_as_pathbuf();
2719+
let mut source = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
2720+
source
2721+
.register_host_function("Echo42", || Ok(1i64))
2722+
.unwrap();
2723+
let mut source = source.evolve().unwrap();
2724+
let snapshot = source.snapshot().unwrap();
2725+
2726+
let path = simple_guest_as_pathbuf();
2727+
let mut target = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
2728+
target
2729+
.register_host_function("Echo42", || Ok(42i64))
2730+
.unwrap();
2731+
let mut target = target.evolve().unwrap();
2732+
2733+
target.restore(snapshot).unwrap();
2734+
assert_eq!(
2735+
target
2736+
.call::<i64>(
2737+
"CallGivenParamlessHostFuncThatReturnsI64",
2738+
"Echo42".to_string(),
2739+
)
2740+
.unwrap(),
2741+
42
2742+
);
2743+
}
2744+
2745+
#[test]
2746+
fn snapshot_restore_recovers_poison_with_different_guest() {
2747+
let mut source = UninitializedSandbox::new(
2748+
GuestBinary::FilePath(c_simple_guest_as_pathbuf()),
2749+
None,
2750+
)
2751+
.unwrap()
2752+
.evolve()
2753+
.unwrap();
2754+
let snapshot = source.snapshot().unwrap();
2755+
2756+
let path = simple_guest_as_pathbuf();
2757+
let mut target = UninitializedSandbox::new(GuestBinary::FilePath(path), None)
2758+
.unwrap()
2759+
.evolve()
2760+
.unwrap();
2761+
assert!(target.call::<()>("ExhaustHeap", ()).is_err());
2762+
assert!(target.status().is_poisoned());
2763+
2764+
target.restore(snapshot).unwrap();
2765+
assert!(!target.status().is_poisoned());
2766+
assert_eq!(target.call::<i32>("StackAllocate", 512i32).unwrap(), 512);
2767+
assert!(matches!(
2768+
target.call::<i32>("GetStatic", ()),
2769+
Err(HyperlightError::GuestError(
2770+
ErrorCode::GuestFunctionNotFound,
2771+
name
2772+
)) if name == "GetStatic"
2773+
));
2774+
}
2775+
25782776
/// Validation runs before any memory or vCPU mutation, so a
25792777
/// rejected `restore` leaves the target usable.
25802778
#[test]

0 commit comments

Comments
 (0)