Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/hyperlight_host/src/hypervisor/hyperlight_vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ pub enum MapRegionError {
MapMemory(#[from] MapMemoryError),
#[error("Region is not page-aligned (page size: {0:#x})")]
NotPageAligned(usize),
#[error(
"Region [{0:#x}..{1:#x}) overlaps existing region [{2:#x}..{3:#x})"
)]
Overlapping(usize, usize, usize, usize),
}

/// Errors that can occur when unmapping a memory region
Expand Down Expand Up @@ -420,6 +424,44 @@ impl HyperlightVm {
return Err(MapRegionError::NotPageAligned(self.page_size));
}

let new_start = region.guest_region.start;
let new_end = region.guest_region.end;

// Check against existing dynamically mapped regions
for (_, existing) in &self.mmap_regions {
if new_start < existing.guest_region.end && new_end > existing.guest_region.start {
return Err(MapRegionError::Overlapping(
new_start,
new_end,
existing.guest_region.start,
existing.guest_region.end,
));
}
}

// Check against the snapshot region
if let Some(ref snapshot) = self.snapshot_memory {
let snap_start = crate::mem::layout::SandboxMemoryLayout::BASE_ADDRESS;
let snap_end = snap_start + snapshot.mem_size();
if new_start < snap_end && new_end > snap_start {
return Err(MapRegionError::Overlapping(
new_start, new_end, snap_start, snap_end,
));
}
}

// Check against the scratch region
if let Some(ref scratch) = self.scratch_memory {
let scratch_start =
hyperlight_common::layout::scratch_base_gpa(scratch.mem_size()) as usize;
let scratch_end = scratch_start + scratch.mem_size();
if new_start < scratch_end && new_end > scratch_start {
return Err(MapRegionError::Overlapping(
new_start, new_end, scratch_start, scratch_end,
));
}
}

// Try to reuse a freed slot first, otherwise use next_slot
let slot = if let Some(freed_slot) = self.freed_slots.pop() {
freed_slot
Expand Down
97 changes: 97 additions & 0 deletions src/hyperlight_host/src/sandbox/initialized_multi_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2551,4 +2551,101 @@ mod tests {
}
let _ = std::fs::remove_file(&path);
}

#[test]
fn map_region_rejects_overlapping_regions() {
let mut sbox: MultiUseSandbox = {
let path = simple_guest_as_string().unwrap();
let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
u_sbox.evolve().unwrap()
};

let mem1 = allocate_guest_memory();
let mem2 = allocate_guest_memory();
let guest_base: usize = 0x200000000;
let region1 = region_for_memory(&mem1, guest_base, MemoryRegionFlags::READ);

// First mapping should succeed
unsafe { sbox.map_region(&region1).unwrap() };

// Exact same range should fail
let region2 = region_for_memory(&mem2, guest_base, MemoryRegionFlags::READ);
let err = unsafe { sbox.map_region(&region2) }.unwrap_err();
assert!(
format!("{err:?}").contains("Overlapping"),
"Expected Overlapping error, got: {err:?}"
);
}

#[test]
fn map_region_rejects_partial_overlap() {
let mut sbox: MultiUseSandbox = {
let path = simple_guest_as_string().unwrap();
let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
u_sbox.evolve().unwrap()
};

let mem1 = allocate_guest_memory();
let mem2 = allocate_guest_memory();
let guest_base: usize = 0x200000000;
let region1 = region_for_memory(&mem1, guest_base, MemoryRegionFlags::READ);
let region_size = mem1.mem_size();

unsafe { sbox.map_region(&region1).unwrap() };

// Overlapping from below (starts before, ends inside)
let overlap_base = guest_base - region_size / 2;
// Align to page boundary
let overlap_base = overlap_base & !(0x1000 - 1);
let region2 = region_for_memory(&mem2, overlap_base, MemoryRegionFlags::READ);
let err = unsafe { sbox.map_region(&region2) }.unwrap_err();
assert!(
format!("{err:?}").contains("Overlapping"),
"Expected Overlapping error for partial overlap, got: {err:?}"
);
}

#[test]
fn map_region_allows_adjacent_non_overlapping() {
let mut sbox: MultiUseSandbox = {
let path = simple_guest_as_string().unwrap();
let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
u_sbox.evolve().unwrap()
};

let mem1 = allocate_guest_memory();
let mem2 = allocate_guest_memory();
let guest_base: usize = 0x200000000;
let region1 = region_for_memory(&mem1, guest_base, MemoryRegionFlags::READ);
let region_size = mem1.mem_size();

unsafe { sbox.map_region(&region1).unwrap() };

// Adjacent region (starts right after the first one ends) should succeed
let adjacent_base = guest_base + region_size;
let region2 = region_for_memory(&mem2, adjacent_base, MemoryRegionFlags::READ);
unsafe { sbox.map_region(&region2).unwrap() };
}

#[test]
fn map_region_rejects_overlap_with_snapshot() {
let mut sbox: MultiUseSandbox = {
let path = simple_guest_as_string().unwrap();
let u_sbox = UninitializedSandbox::new(GuestBinary::FilePath(path), None).unwrap();
u_sbox.evolve().unwrap()
};

// Try to map at BASE_ADDRESS (0x1000) which overlaps the snapshot region
let mem = allocate_guest_memory();
let region = region_for_memory(
&mem,
crate::mem::layout::SandboxMemoryLayout::BASE_ADDRESS,
MemoryRegionFlags::READ,
);
let err = unsafe { sbox.map_region(&region) }.unwrap_err();
assert!(
format!("{err:?}").contains("Overlapping"),
"Expected Overlapping error for snapshot overlap, got: {err:?}"
);
}
}