@@ -568,9 +568,10 @@ impl MultiUseSandbox {
568568 // writes can be rolled back when necessary.
569569 log_then_return ! ( "TODO: Writable mappings not yet supported" ) ;
570570 }
571- // Reset snapshot since we are mutating the sandbox state
572- self . snapshot = None ;
571+
572+ // Map first so overlaps are rejected before resetting the snapshot
573573 unsafe { self . vm . map_region ( rgn) } . map_err ( HyperlightVmError :: MapRegion ) ?;
574+ self . snapshot = None ;
574575 self . mem_mgr . mapped_rgns += 1 ;
575576 Ok ( ( ) )
576577 }
@@ -642,25 +643,12 @@ impl MultiUseSandbox {
642643 // Phase 2: VM-side work (map into guest address space)
643644 let region = prepared. to_memory_region ( ) ?;
644645
645- // Check for overlaps with existing file mappings in the VM.
646- for existing_region in self . vm . get_mapped_regions ( ) {
647- let ex_start = existing_region. guest_region . start as u64 ;
648- let ex_end = existing_region. guest_region . end as u64 ;
649- if guest_base < ex_end && mapping_end > ex_start {
650- return Err ( crate :: HyperlightError :: Error ( format ! (
651- "map_file_cow: mapping [{:#x}..{:#x}) overlaps existing mapping [{:#x}..{:#x})" ,
652- guest_base, mapping_end, ex_start, ex_end,
653- ) ) ) ;
654- }
655- }
656-
657- // Reset snapshot since we are mutating the sandbox state
658- self . snapshot = None ;
659-
660646 unsafe { self . vm . map_region ( & region) }
661647 . map_err ( HyperlightVmError :: MapRegion )
662648 . map_err ( crate :: HyperlightError :: HyperlightVmError ) ?;
663649
650+ self . snapshot = None ;
651+
664652 let size = prepared. size as u64 ;
665653
666654 // Mark consumed immediately after map_region succeeds.
@@ -2588,4 +2576,120 @@ mod tests {
25882576 }
25892577 let _ = std:: fs:: remove_file ( & path) ;
25902578 }
2579+
2580+ #[ test]
2581+ fn map_region_rejects_overlapping_regions ( ) {
2582+ let mut sbox: MultiUseSandbox = {
2583+ let path = simple_guest_as_string ( ) . unwrap ( ) ;
2584+ let u_sbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None ) . unwrap ( ) ;
2585+ u_sbox. evolve ( ) . unwrap ( )
2586+ } ;
2587+
2588+ let mem1 = allocate_guest_memory ( ) ;
2589+ let mem2 = allocate_guest_memory ( ) ;
2590+ let guest_base: usize = 0x200000000 ;
2591+ let region1 = region_for_memory ( & mem1, guest_base, MemoryRegionFlags :: READ ) ;
2592+
2593+ // First mapping should succeed
2594+ unsafe { sbox. map_region ( & region1) . unwrap ( ) } ;
2595+
2596+ // Exact same range should fail
2597+ let region2 = region_for_memory ( & mem2, guest_base, MemoryRegionFlags :: READ ) ;
2598+ let err = unsafe { sbox. map_region ( & region2) } . unwrap_err ( ) ;
2599+ assert ! (
2600+ format!( "{err:?}" ) . contains( "Overlapping" ) ,
2601+ "Expected Overlapping error, got: {err:?}"
2602+ ) ;
2603+ }
2604+
2605+ #[ test]
2606+ fn map_region_rejects_partial_overlap ( ) {
2607+ let mut sbox: MultiUseSandbox = {
2608+ let path = simple_guest_as_string ( ) . unwrap ( ) ;
2609+ let u_sbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None ) . unwrap ( ) ;
2610+ u_sbox. evolve ( ) . unwrap ( )
2611+ } ;
2612+
2613+ // Use multi-page regions so partial overlap is geometrically possible
2614+ let mem1 = page_aligned_memory ( & [ 0xAA ; 8192 ] ) ; // 2 pages
2615+ let mem2 = page_aligned_memory ( & [ 0xBB ; 8192 ] ) ; // 2 pages
2616+ let guest_base: usize = 0x200000000 ;
2617+ let region1 = region_for_memory ( & mem1, guest_base, MemoryRegionFlags :: READ ) ;
2618+
2619+ unsafe { sbox. map_region ( & region1) . unwrap ( ) } ;
2620+
2621+ // region2 starts one page before region1, overlapping by one page
2622+ let overlap_base = guest_base - 0x1000 ;
2623+ let region2 = region_for_memory ( & mem2, overlap_base, MemoryRegionFlags :: READ ) ;
2624+ let err = unsafe { sbox. map_region ( & region2) } . unwrap_err ( ) ;
2625+ assert ! (
2626+ format!( "{err:?}" ) . contains( "verlap" ) ,
2627+ "Expected overlap error for partial overlap, got: {err:?}"
2628+ ) ;
2629+ }
2630+
2631+ #[ test]
2632+ fn map_region_allows_adjacent_non_overlapping ( ) {
2633+ let mut sbox: MultiUseSandbox = {
2634+ let path = simple_guest_as_string ( ) . unwrap ( ) ;
2635+ let u_sbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None ) . unwrap ( ) ;
2636+ u_sbox. evolve ( ) . unwrap ( )
2637+ } ;
2638+
2639+ let mem1 = allocate_guest_memory ( ) ;
2640+ let mem2 = allocate_guest_memory ( ) ;
2641+ let guest_base: usize = 0x200000000 ;
2642+ let region1 = region_for_memory ( & mem1, guest_base, MemoryRegionFlags :: READ ) ;
2643+ let region_size = mem1. mem_size ( ) ;
2644+
2645+ unsafe { sbox. map_region ( & region1) . unwrap ( ) } ;
2646+
2647+ // Adjacent region (starts right after the first one ends) should succeed
2648+ let adjacent_base = guest_base + region_size;
2649+ let region2 = region_for_memory ( & mem2, adjacent_base, MemoryRegionFlags :: READ ) ;
2650+ unsafe { sbox. map_region ( & region2) . unwrap ( ) } ;
2651+ }
2652+
2653+ #[ test]
2654+ fn map_region_rejects_overlap_with_snapshot ( ) {
2655+ let mut sbox: MultiUseSandbox = {
2656+ let path = simple_guest_as_string ( ) . unwrap ( ) ;
2657+ let u_sbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None ) . unwrap ( ) ;
2658+ u_sbox. evolve ( ) . unwrap ( )
2659+ } ;
2660+
2661+ // Try to map at BASE_ADDRESS (0x1000) which overlaps the snapshot region
2662+ let mem = allocate_guest_memory ( ) ;
2663+ let region = region_for_memory (
2664+ & mem,
2665+ crate :: mem:: layout:: SandboxMemoryLayout :: BASE_ADDRESS ,
2666+ MemoryRegionFlags :: READ ,
2667+ ) ;
2668+ let err = unsafe { sbox. map_region ( & region) } . unwrap_err ( ) ;
2669+ assert ! (
2670+ format!( "{err:?}" ) . contains( "Overlapping" ) ,
2671+ "Expected Overlapping error for snapshot overlap, got: {err:?}"
2672+ ) ;
2673+ }
2674+
2675+ #[ test]
2676+ fn map_region_rejects_overlap_with_scratch ( ) {
2677+ let mut sbox: MultiUseSandbox = {
2678+ let path = simple_guest_as_string ( ) . unwrap ( ) ;
2679+ let u_sbox = UninitializedSandbox :: new ( GuestBinary :: FilePath ( path) , None ) . unwrap ( ) ;
2680+ u_sbox. evolve ( ) . unwrap ( )
2681+ } ;
2682+
2683+ // The scratch region occupies the top of the GPA space
2684+ let scratch_addr = hyperlight_common:: layout:: scratch_base_gpa (
2685+ crate :: sandbox:: SandboxConfiguration :: DEFAULT_SCRATCH_SIZE ,
2686+ ) as usize ;
2687+ let mem = allocate_guest_memory ( ) ;
2688+ let region = region_for_memory ( & mem, scratch_addr, MemoryRegionFlags :: READ ) ;
2689+ let err = unsafe { sbox. map_region ( & region) } . unwrap_err ( ) ;
2690+ assert ! (
2691+ format!( "{err:?}" ) . contains( "verlap" ) ,
2692+ "Expected overlap error for scratch region, got: {err:?}"
2693+ ) ;
2694+ }
25912695}
0 commit comments