@@ -136,7 +136,7 @@ pub struct MultiUseSandbox {
136136///
137137/// Returns a list of root page table GPAs to walk. If the list is
138138/// empty, only `root_pt_gpa` is used.
139- pub type PtRootFinder = Box < dyn Fn ( & [ u8 ] , & [ u8 ] , u64 ) -> Vec < u64 > + Send > ;
139+ pub type PtRootFinder = Arc < dyn Fn ( & [ u8 ] , & [ u8 ] , u64 ) -> Vec < u64 > + Send + Sync > ;
140140
141141impl MultiUseSandbox {
142142 fn ensure_usable ( & self ) -> Result < ( ) > {
@@ -180,8 +180,12 @@ impl MultiUseSandbox {
180180 /// Set a callback that discovers page table roots from guest memory.
181181 /// The callback receives (snapshot_mem, scratch_mem, cr3) and returns
182182 /// the list of root GPAs to walk during snapshot creation.
183+ ///
184+ /// In-memory snapshots retain the finder across restore. The finder is not
185+ /// serialized.
183186 pub fn set_pt_root_finder ( & mut self , finder : PtRootFinder ) {
184187 self . pt_root_finder = Some ( finder) ;
188+ self . snapshot = None ;
185189 }
186190
187191 /// Create a `MultiUseSandbox` directly from a [`Snapshot`],
@@ -365,13 +369,14 @@ impl MultiUseSandbox {
365369 #[ cfg( gdb) ]
366370 let dbg_mem_wrapper = Arc :: new ( Mutex :: new ( hshm. clone ( ) ) ) ;
367371
368- let sbox = MultiUseSandbox :: from_uninit (
372+ let mut sbox = MultiUseSandbox :: from_uninit (
369373 host_funcs,
370374 hshm,
371375 vm,
372376 #[ cfg( gdb) ]
373377 dbg_mem_wrapper,
374378 ) ;
379+ sbox. pt_root_finder = snapshot. pt_root_finder ( ) . cloned ( ) ;
375380 Ok ( sbox)
376381 }
377382
@@ -462,6 +467,7 @@ impl MultiUseSandbox {
462467 msrs,
463468 next_action,
464469 host_functions,
470+ self . pt_root_finder . clone ( ) ,
465471 ) ?;
466472 let snapshot = Arc :: new ( memory_snapshot) ;
467473 self . snapshot = Some ( snapshot. clone ( ) ) ;
@@ -682,7 +688,7 @@ impl MultiUseSandbox {
682688 self . vm . clear_crashdump_binary_path ( ) ;
683689 }
684690
685- self . pt_root_finder = None ;
691+ self . pt_root_finder = snapshot . pt_root_finder ( ) . cloned ( ) ;
686692
687693 // The restored snapshot is now our most current snapshot
688694 self . snapshot = Some ( snapshot. clone ( ) ) ;
@@ -1249,6 +1255,7 @@ fn warn_on_layout_override(
12491255
12501256#[ cfg( test) ]
12511257mod tests {
1258+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
12521259 use std:: sync:: { Arc , Barrier } ;
12531260 use std:: thread;
12541261
@@ -1262,6 +1269,7 @@ mod tests {
12621269 use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags , MemoryRegionType } ;
12631270 use crate :: mem:: shared_mem:: { ExclusiveSharedMemory , GuestSharedMemory , SharedMemory as _} ;
12641271 use crate :: sandbox:: SandboxConfiguration ;
1272+ use crate :: sandbox:: snapshot:: Snapshot ;
12651273 use crate :: sandbox:: uninitialized:: { GuestBlob , GuestEnvironment } ;
12661274 use crate :: {
12671275 GuestBinary , HyperlightError , MultiUseSandbox , Result , SandboxStatus , UninitializedSandbox ,
@@ -1282,6 +1290,23 @@ mod tests {
12821290 assert ! ( SandboxStatus :: Unrecoverable . is_unrecoverable( ) ) ;
12831291 }
12841292
1293+ trait AmbiguousIfSync < Marker > {
1294+ fn assert_not_sync ( ) { }
1295+ }
1296+
1297+ impl < T : ?Sized > AmbiguousIfSync < ( ) > for T { }
1298+ impl < T : ?Sized + Sync > AmbiguousIfSync < u8 > for T { }
1299+
1300+ #[ test]
1301+ fn snapshot_and_sandbox_thread_safety ( ) {
1302+ fn assert_send < T : Send > ( ) { }
1303+ fn assert_send_sync < T : Send + Sync > ( ) { }
1304+
1305+ assert_send :: < MultiUseSandbox > ( ) ;
1306+ let _ = <MultiUseSandbox as AmbiguousIfSync < _ > >:: assert_not_sync;
1307+ assert_send_sync :: < Snapshot > ( ) ;
1308+ }
1309+
12851310 #[ test]
12861311 fn poison ( ) {
12871312 let mut sbox: MultiUseSandbox = {
@@ -2590,6 +2615,8 @@ mod tests {
25902615 . unwrap ( )
25912616 . evolve ( )
25922617 . unwrap ( ) ;
2618+ let source_finder: crate :: sandbox:: PtRootFinder = Arc :: new ( |_, _, root| vec ! [ root] ) ;
2619+ source. set_pt_root_finder ( source_finder. clone ( ) ) ;
25932620 let mut target =
25942621 UninitializedSandbox :: new ( GuestBinary :: FilePath ( simple_guest_as_pathbuf ( ) ) , None )
25952622 . unwrap ( )
@@ -2598,8 +2625,7 @@ mod tests {
25982625
25992626 assert_eq ! ( source. call:: <i32 >( "StackAllocate" , 256i32 ) . unwrap( ) , 256 ) ;
26002627 assert_eq ! ( target. call:: <i32 >( "AddToStatic" , 17i32 ) . unwrap( ) , 17 ) ;
2601- target. set_pt_root_finder ( Box :: new ( |_, _, root| vec ! [ root] ) ) ;
2602- assert ! ( target. pt_root_finder. is_some( ) ) ;
2628+ target. set_pt_root_finder ( Arc :: new ( |_, _, _| Vec :: new ( ) ) ) ;
26032629
26042630 assert_ne ! (
26052631 source. mem_mgr. layout. code_size( ) ,
@@ -2616,7 +2642,10 @@ mod tests {
26162642
26172643 let snapshot = source. snapshot ( ) . unwrap ( ) ;
26182644 target. restore ( snapshot) . unwrap ( ) ;
2619- assert ! ( target. pt_root_finder. is_none( ) ) ;
2645+ assert ! ( Arc :: ptr_eq(
2646+ target. pt_root_finder. as_ref( ) . unwrap( ) ,
2647+ & source_finder
2648+ ) ) ;
26202649 assert_eq ! ( target. call:: <i32 >( "StackAllocate" , 512i32 ) . unwrap( ) , 512 ) ;
26212650 assert ! ( matches!(
26222651 target. call:: <i32 >( "GetStatic" , ( ) ) ,
@@ -2627,6 +2656,68 @@ mod tests {
26272656 ) ) ;
26282657 }
26292658
2659+ #[ test]
2660+ fn snapshot_restore_clears_absent_pt_root_finder ( ) {
2661+ let path = simple_guest_as_pathbuf ( ) ;
2662+ let mut source = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2663+ . unwrap ( )
2664+ . evolve ( )
2665+ . unwrap ( ) ;
2666+ let snapshot = source. snapshot ( ) . unwrap ( ) ;
2667+ assert ! ( snapshot. pt_root_finder( ) . is_none( ) ) ;
2668+
2669+ let path = simple_guest_as_pathbuf ( ) ;
2670+ let mut target = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2671+ . unwrap ( )
2672+ . evolve ( )
2673+ . unwrap ( ) ;
2674+ target. set_pt_root_finder ( Arc :: new ( |_, _, root| vec ! [ root] ) ) ;
2675+
2676+ target. restore ( snapshot) . unwrap ( ) ;
2677+ assert ! ( target. pt_root_finder. is_none( ) ) ;
2678+ }
2679+
2680+ #[ test]
2681+ fn snapshot_restore_uses_retained_pt_root_finder ( ) {
2682+ let source_calls = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
2683+ let source_calls_in_finder = source_calls. clone ( ) ;
2684+ let source_finder: crate :: sandbox:: PtRootFinder = Arc :: new ( move |_, _, _| {
2685+ source_calls_in_finder. fetch_add ( 1 , Ordering :: Relaxed ) ;
2686+ Vec :: new ( )
2687+ } ) ;
2688+ let path = simple_guest_as_pathbuf ( ) ;
2689+ let mut source = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2690+ . unwrap ( )
2691+ . evolve ( )
2692+ . unwrap ( ) ;
2693+ source. set_pt_root_finder ( source_finder) ;
2694+ let snapshot = source. snapshot ( ) . unwrap ( ) ;
2695+
2696+ let target_calls = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
2697+ let target_calls_in_finder = target_calls. clone ( ) ;
2698+ let target_finder: crate :: sandbox:: PtRootFinder = Arc :: new ( move |_, _, root| {
2699+ target_calls_in_finder. fetch_add ( 1 , Ordering :: Relaxed ) ;
2700+ vec ! [ root]
2701+ } ) ;
2702+ let path = simple_guest_as_pathbuf ( ) ;
2703+ let mut target = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2704+ . unwrap ( )
2705+ . evolve ( )
2706+ . unwrap ( ) ;
2707+ target. set_pt_root_finder ( target_finder) ;
2708+ target. restore ( snapshot) . unwrap ( ) ;
2709+
2710+ let source_calls_before = source_calls. load ( Ordering :: Relaxed ) ;
2711+ target. call :: < i32 > ( "GetStatic" , ( ) ) . unwrap ( ) ;
2712+ target. snapshot ( ) . unwrap ( ) ;
2713+
2714+ assert_eq ! (
2715+ source_calls. load( Ordering :: Relaxed ) ,
2716+ source_calls_before + 1
2717+ ) ;
2718+ assert_eq ! ( target_calls. load( Ordering :: Relaxed ) , 0 ) ;
2719+ }
2720+
26302721 #[ test]
26312722 fn snapshot_restore_replaces_c_guest_with_rust_guest ( ) {
26322723 let mut source =
0 commit comments