Skip to content

Commit fefb1bc

Browse files
committed
[fix] Make sure memory is not mapped writeable into sandbox in kvm
Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 6264bb0 commit fefb1bc

File tree

4 files changed

+98
-10
lines changed

4 files changed

+98
-10
lines changed

src/hyperlight_host/src/mem/memory_region.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,8 @@ impl From<MemoryRegion> for mshv_user_mem_region {
314314
#[cfg(kvm)]
315315
impl From<MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
316316
fn from(region: MemoryRegion) -> Self {
317+
use bitflags::bitflags_match;
318+
317319
let perm_flags =
318320
MemoryRegionFlags::READ | MemoryRegionFlags::WRITE | MemoryRegionFlags::EXECUTE;
319321

@@ -324,10 +326,11 @@ impl From<MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
324326
guest_phys_addr: region.guest_region.start as u64,
325327
memory_size: (region.guest_region.end - region.guest_region.start) as u64,
326328
userspace_addr: region.host_region.start as u64,
327-
flags: match perm_flags {
329+
flags: bitflags_match!(perm_flags, {
328330
MemoryRegionFlags::READ => KVM_MEM_READONLY,
331+
MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE => KVM_MEM_READONLY, // KVM_MEM_READONLY is executable
329332
_ => 0, // normal, RWX
330-
},
333+
}),
331334
}
332335
}
333336
}

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -605,8 +605,12 @@ mod tests {
605605
let guest_base = 0x1_0000_0000; // Arbitrary guest base address
606606

607607
unsafe {
608-
sbox.map_region(&region_for_memory(&map_mem, guest_base))
609-
.unwrap();
608+
sbox.map_region(&region_for_memory(
609+
&map_mem,
610+
guest_base,
611+
MemoryRegionFlags::READ,
612+
))
613+
.unwrap();
610614
}
611615

612616
let _guard = map_mem.lock.try_read().unwrap();
@@ -620,6 +624,47 @@ mod tests {
620624
assert_eq!(actual, expected);
621625
}
622626

627+
// Makes sure MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE is not writable
628+
#[cfg(target_os = "linux")]
629+
#[test]
630+
fn test_mmap_write() {
631+
let mut sbox = UninitializedSandbox::new(
632+
GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
633+
None,
634+
)
635+
.unwrap()
636+
.evolve()
637+
.unwrap();
638+
639+
let expected = b"hello world";
640+
let map_mem = page_aligned_memory(expected);
641+
let guest_base = 0x1_0000_0000; // Arbitrary guest base address
642+
643+
unsafe {
644+
sbox.map_region(&region_for_memory(
645+
&map_mem,
646+
guest_base,
647+
MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE,
648+
))
649+
.unwrap();
650+
}
651+
652+
let _guard = map_mem.lock.try_read().unwrap();
653+
654+
// write should fail because the memory is mapped as read-only
655+
let err = sbox
656+
.call_guest_function_by_name::<bool>(
657+
"WriteMappedBuffer",
658+
(guest_base as u64, expected.len() as u64),
659+
)
660+
.unwrap_err();
661+
662+
match err {
663+
HyperlightError::MemoryAccessViolation(addr, ..) if addr == guest_base as u64 => {}
664+
_ => panic!("Expected MemoryAccessViolation error"),
665+
};
666+
}
667+
623668
#[cfg(target_os = "linux")]
624669
fn page_aligned_memory(src: &[u8]) -> GuestSharedMemory {
625670
use hyperlight_common::mem::PAGE_SIZE_USIZE;
@@ -635,13 +680,17 @@ mod tests {
635680
}
636681

637682
#[cfg(target_os = "linux")]
638-
fn region_for_memory(mem: &GuestSharedMemory, guest_base: usize) -> MemoryRegion {
683+
fn region_for_memory(
684+
mem: &GuestSharedMemory,
685+
guest_base: usize,
686+
flags: MemoryRegionFlags,
687+
) -> MemoryRegion {
639688
let ptr = mem.base_addr();
640689
let len = mem.mem_size();
641690
MemoryRegion {
642691
host_region: ptr..(ptr + len),
643692
guest_region: guest_base..(guest_base + len),
644-
flags: MemoryRegionFlags::READ,
693+
flags,
645694
region_type: MemoryRegionType::Heap,
646695
}
647696
}

src/tests/rust_guests/simpleguest/src/main.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,33 @@ fn read_mapped_buffer(function_call: &FunctionCall) -> Result<Vec<u8>> {
798798
}
799799
}
800800

801+
fn write_mapped_buffer(function_call: &FunctionCall) -> Result<Vec<u8>> {
802+
if let (ParameterValue::ULong(base), ParameterValue::ULong(len)) = (
803+
function_call.parameters.clone().unwrap()[0].clone(),
804+
function_call.parameters.clone().unwrap()[1].clone(),
805+
) {
806+
let base = base as usize as *mut u8;
807+
let len = len as usize;
808+
809+
unsafe {
810+
hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096)
811+
};
812+
813+
let data = unsafe { core::slice::from_raw_parts_mut(base, len) };
814+
815+
// should fail
816+
data[0] = 0x42;
817+
818+
// should never reach this
819+
Ok(get_flatbuffer_result(true))
820+
} else {
821+
Err(HyperlightGuestError::new(
822+
ErrorCode::GuestFunctionParameterTypeMismatch,
823+
"Invalid parameters passed to read_mapped_buffer".to_string(),
824+
))
825+
}
826+
}
827+
801828
#[no_mangle]
802829
pub extern "C" fn hyperlight_main() {
803830
let read_from_user_memory_def = GuestFunctionDefinition::new(
@@ -818,6 +845,15 @@ pub extern "C" fn hyperlight_main() {
818845

819846
register_function(read_mapped_buffer_def);
820847

848+
let write_mapped_buffer_def = GuestFunctionDefinition::new(
849+
"WriteMappedBuffer".to_string(),
850+
Vec::from(&[ParameterType::ULong, ParameterType::ULong]),
851+
ReturnType::Bool,
852+
write_mapped_buffer as usize,
853+
);
854+
855+
register_function(write_mapped_buffer_def);
856+
821857
let set_static_def = GuestFunctionDefinition::new(
822858
"SetStatic".to_string(),
823859
Vec::new(),

src/tests/rust_guests/witguest/Cargo.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)