@@ -116,7 +116,7 @@ pub struct MultiUseSandbox {
116116///
117117/// Returns a list of root page table GPAs to walk. If the list is
118118/// empty, only `root_pt_gpa` is used.
119- pub type PtRootFinder = Box < dyn Fn ( & [ u8 ] , & [ u8 ] , u64 ) -> Vec < u64 > + Send > ;
119+ pub type PtRootFinder = Arc < dyn Fn ( & [ u8 ] , & [ u8 ] , u64 ) -> Vec < u64 > + Send + Sync > ;
120120
121121impl MultiUseSandbox {
122122 fn ensure_usable ( & self ) -> Result < ( ) > {
@@ -157,8 +157,12 @@ impl MultiUseSandbox {
157157 /// Set a callback that discovers page table roots from guest memory.
158158 /// The callback receives (snapshot_mem, scratch_mem, cr3) and returns
159159 /// the list of root GPAs to walk during snapshot creation.
160+ ///
161+ /// In-memory snapshots retain the finder across restore. The finder is not
162+ /// serialized.
160163 pub fn set_pt_root_finder ( & mut self , finder : PtRootFinder ) {
161164 self . pt_root_finder = Some ( finder) ;
165+ self . snapshot = None ;
162166 }
163167
164168 /// Create a `MultiUseSandbox` directly from a [`Snapshot`],
@@ -328,7 +332,8 @@ impl MultiUseSandbox {
328332 } ) ?;
329333 }
330334
331- let sbox = MultiUseSandbox :: from_uninit ( host_funcs, hshm, vm) ;
335+ let mut sbox = MultiUseSandbox :: from_uninit ( host_funcs, hshm, vm) ;
336+ sbox. pt_root_finder = snapshot. pt_root_finder ( ) . cloned ( ) ;
332337 Ok ( sbox)
333338 }
334339
@@ -420,6 +425,7 @@ impl MultiUseSandbox {
420425 msrs,
421426 next_action,
422427 host_functions,
428+ self . pt_root_finder . clone ( ) ,
423429 ) ?;
424430 let snapshot = Arc :: new ( memory_snapshot) ;
425431 self . snapshot = Some ( snapshot. clone ( ) ) ;
@@ -613,7 +619,7 @@ impl MultiUseSandbox {
613619 self . vm . clear_crashdump_binary_path ( ) ;
614620 }
615621
616- self . pt_root_finder = None ;
622+ self . pt_root_finder = snapshot . pt_root_finder ( ) . cloned ( ) ;
617623
618624 // The restored snapshot is now our most current snapshot
619625 self . snapshot = Some ( snapshot. clone ( ) ) ;
@@ -1183,6 +1189,7 @@ fn warn_on_layout_override(
11831189
11841190#[ cfg( test) ]
11851191mod tests {
1192+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
11861193 use std:: sync:: { Arc , Barrier } ;
11871194 use std:: thread;
11881195
@@ -1196,6 +1203,7 @@ mod tests {
11961203 use crate :: mem:: memory_region:: { MemoryRegion , MemoryRegionFlags , MemoryRegionType } ;
11971204 use crate :: mem:: shared_mem:: { ExclusiveSharedMemory , GuestSharedMemory , SharedMemory as _} ;
11981205 use crate :: sandbox:: SandboxConfiguration ;
1206+ use crate :: sandbox:: snapshot:: Snapshot ;
11991207 use crate :: sandbox:: uninitialized:: { GuestBlob , GuestEnvironment } ;
12001208 use crate :: {
12011209 GuestBinary , HyperlightError , MultiUseSandbox , Result , SandboxStatus , UninitializedSandbox ,
@@ -1216,6 +1224,23 @@ mod tests {
12161224 assert ! ( SandboxStatus :: Unrecoverable . is_unrecoverable( ) ) ;
12171225 }
12181226
1227+ trait AmbiguousIfSync < Marker > {
1228+ fn assert_not_sync ( ) { }
1229+ }
1230+
1231+ impl < T : ?Sized > AmbiguousIfSync < ( ) > for T { }
1232+ impl < T : ?Sized + Sync > AmbiguousIfSync < u8 > for T { }
1233+
1234+ #[ test]
1235+ fn snapshot_and_sandbox_thread_safety ( ) {
1236+ fn assert_send < T : Send > ( ) { }
1237+ fn assert_send_sync < T : Send + Sync > ( ) { }
1238+
1239+ assert_send :: < MultiUseSandbox > ( ) ;
1240+ let _ = <MultiUseSandbox as AmbiguousIfSync < _ > >:: assert_not_sync;
1241+ assert_send_sync :: < Snapshot > ( ) ;
1242+ }
1243+
12191244 #[ test]
12201245 fn poison ( ) {
12211246 let mut sbox: MultiUseSandbox = {
@@ -2275,6 +2300,8 @@ mod tests {
22752300 . unwrap ( )
22762301 . evolve ( )
22772302 . unwrap ( ) ;
2303+ let source_finder: crate :: sandbox:: PtRootFinder = Arc :: new ( |_, _, root| vec ! [ root] ) ;
2304+ source. set_pt_root_finder ( source_finder. clone ( ) ) ;
22782305 let mut target =
22792306 UninitializedSandbox :: new ( GuestBinary :: FilePath ( simple_guest_as_pathbuf ( ) ) , None )
22802307 . unwrap ( )
@@ -2283,8 +2310,7 @@ mod tests {
22832310
22842311 assert_eq ! ( source. call:: <i32 >( "StackAllocate" , 256i32 ) . unwrap( ) , 256 ) ;
22852312 assert_eq ! ( target. call:: <i32 >( "AddToStatic" , 17i32 ) . unwrap( ) , 17 ) ;
2286- target. set_pt_root_finder ( Box :: new ( |_, _, root| vec ! [ root] ) ) ;
2287- assert ! ( target. pt_root_finder. is_some( ) ) ;
2313+ target. set_pt_root_finder ( Arc :: new ( |_, _, _| Vec :: new ( ) ) ) ;
22882314
22892315 assert_ne ! (
22902316 source. mem_mgr. layout. code_size( ) ,
@@ -2301,7 +2327,10 @@ mod tests {
23012327
23022328 let snapshot = source. snapshot ( ) . unwrap ( ) ;
23032329 target. restore ( snapshot) . unwrap ( ) ;
2304- assert ! ( target. pt_root_finder. is_none( ) ) ;
2330+ assert ! ( Arc :: ptr_eq(
2331+ target. pt_root_finder. as_ref( ) . unwrap( ) ,
2332+ & source_finder
2333+ ) ) ;
23052334 assert_eq ! ( target. call:: <i32 >( "StackAllocate" , 512i32 ) . unwrap( ) , 512 ) ;
23062335 assert ! ( matches!(
23072336 target. call:: <i32 >( "GetStatic" , ( ) ) ,
@@ -2312,6 +2341,68 @@ mod tests {
23122341 ) ) ;
23132342 }
23142343
2344+ #[ test]
2345+ fn snapshot_restore_clears_absent_pt_root_finder ( ) {
2346+ let path = simple_guest_as_pathbuf ( ) ;
2347+ let mut source = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2348+ . unwrap ( )
2349+ . evolve ( )
2350+ . unwrap ( ) ;
2351+ let snapshot = source. snapshot ( ) . unwrap ( ) ;
2352+ assert ! ( snapshot. pt_root_finder( ) . is_none( ) ) ;
2353+
2354+ let path = simple_guest_as_pathbuf ( ) ;
2355+ let mut target = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2356+ . unwrap ( )
2357+ . evolve ( )
2358+ . unwrap ( ) ;
2359+ target. set_pt_root_finder ( Arc :: new ( |_, _, root| vec ! [ root] ) ) ;
2360+
2361+ target. restore ( snapshot) . unwrap ( ) ;
2362+ assert ! ( target. pt_root_finder. is_none( ) ) ;
2363+ }
2364+
2365+ #[ test]
2366+ fn snapshot_restore_uses_retained_pt_root_finder ( ) {
2367+ let source_calls = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
2368+ let source_calls_in_finder = source_calls. clone ( ) ;
2369+ let source_finder: crate :: sandbox:: PtRootFinder = Arc :: new ( move |_, _, _| {
2370+ source_calls_in_finder. fetch_add ( 1 , Ordering :: Relaxed ) ;
2371+ Vec :: new ( )
2372+ } ) ;
2373+ let path = simple_guest_as_pathbuf ( ) ;
2374+ let mut source = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2375+ . unwrap ( )
2376+ . evolve ( )
2377+ . unwrap ( ) ;
2378+ source. set_pt_root_finder ( source_finder) ;
2379+ let snapshot = source. snapshot ( ) . unwrap ( ) ;
2380+
2381+ let target_calls = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
2382+ let target_calls_in_finder = target_calls. clone ( ) ;
2383+ let target_finder: crate :: sandbox:: PtRootFinder = Arc :: new ( move |_, _, root| {
2384+ target_calls_in_finder. fetch_add ( 1 , Ordering :: Relaxed ) ;
2385+ vec ! [ root]
2386+ } ) ;
2387+ let path = simple_guest_as_pathbuf ( ) ;
2388+ let mut target = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None )
2389+ . unwrap ( )
2390+ . evolve ( )
2391+ . unwrap ( ) ;
2392+ target. set_pt_root_finder ( target_finder) ;
2393+ target. restore ( snapshot) . unwrap ( ) ;
2394+
2395+ let source_calls_before = source_calls. load ( Ordering :: Relaxed ) ;
2396+ target. call :: < i32 > ( "GetStatic" , ( ) ) . unwrap ( ) ;
2397+ target. snapshot ( ) . unwrap ( ) ;
2398+
2399+ assert_eq ! (
2400+ source_calls. load( Ordering :: Relaxed ) ,
2401+ source_calls_before + 1
2402+ ) ;
2403+ assert_eq ! ( target_calls. load( Ordering :: Relaxed ) , 0 ) ;
2404+ }
2405+
23152406 #[ test]
23162407 fn snapshot_restore_replaces_c_guest_with_rust_guest ( ) {
23172408 let mut source =
0 commit comments