Skip to content

Commit dfe7772

Browse files
authored
[fix] Make sure memory is not mapped writeable into sandbox in kvm (#740)
Signed-off-by: Ludvig Liljenberg <4257730+ludfjig@users.noreply.github.com>
1 parent a32e8b4 commit dfe7772

File tree

3 files changed

+139
-7
lines changed

3 files changed

+139
-7
lines changed

src/hyperlight_host/src/mem/memory_region.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,11 @@ impl From<MemoryRegion> for kvm_bindings::kvm_userspace_memory_region {
324324
guest_phys_addr: region.guest_region.start as u64,
325325
memory_size: (region.guest_region.end - region.guest_region.start) as u64,
326326
userspace_addr: region.host_region.start as u64,
327-
flags: match perm_flags {
328-
MemoryRegionFlags::READ => KVM_MEM_READONLY,
329-
_ => 0, // normal, RWX
327+
flags: if perm_flags.contains(MemoryRegionFlags::WRITE) {
328+
0 // RWX
329+
} else {
330+
// Note: KVM_MEM_READONLY is executable
331+
KVM_MEM_READONLY // RX
330332
},
331333
}
332334
}

src/hyperlight_host/src/sandbox/initialized_multi_use.rs

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

598598
unsafe {
599-
sbox.map_region(&region_for_memory(&map_mem, guest_base))
600-
.unwrap();
599+
sbox.map_region(&region_for_memory(
600+
&map_mem,
601+
guest_base,
602+
MemoryRegionFlags::READ,
603+
))
604+
.unwrap();
601605
}
602606

603607
let _guard = map_mem.lock.try_read().unwrap();
@@ -611,6 +615,56 @@ mod tests {
611615
assert_eq!(actual, expected);
612616
}
613617

618+
// Makes sure MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE executable but not writable
619+
#[cfg(target_os = "linux")]
620+
#[test]
621+
fn test_mmap_write_exec() {
622+
let mut sbox = UninitializedSandbox::new(
623+
GuestBinary::FilePath(simple_guest_as_string().expect("Guest Binary Missing")),
624+
None,
625+
)
626+
.unwrap()
627+
.evolve()
628+
.unwrap();
629+
630+
let expected = &[0x90, 0x90, 0x90, 0xC3]; // NOOP slide to RET
631+
let map_mem = page_aligned_memory(expected);
632+
let guest_base = 0x1_0000_0000; // Arbitrary guest base address
633+
634+
unsafe {
635+
sbox.map_region(&region_for_memory(
636+
&map_mem,
637+
guest_base,
638+
MemoryRegionFlags::READ | MemoryRegionFlags::EXECUTE,
639+
))
640+
.unwrap();
641+
}
642+
643+
let _guard = map_mem.lock.try_read().unwrap();
644+
645+
// Execute should pass since memory is executable
646+
let succeed = sbox
647+
.call_guest_function_by_name::<bool>(
648+
"ExecMappedBuffer",
649+
(guest_base as u64, expected.len() as u64),
650+
)
651+
.unwrap();
652+
assert!(succeed, "Expected execution of mapped buffer to succeed");
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+
614668
#[cfg(target_os = "linux")]
615669
fn page_aligned_memory(src: &[u8]) -> GuestSharedMemory {
616670
use hyperlight_common::mem::PAGE_SIZE_USIZE;
@@ -626,13 +680,17 @@ mod tests {
626680
}
627681

628682
#[cfg(target_os = "linux")]
629-
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 {
630688
let ptr = mem.base_addr();
631689
let len = mem.mem_size();
632690
MemoryRegion {
633691
host_region: ptr..(ptr + len),
634692
guest_region: guest_base..(guest_base + len),
635-
flags: MemoryRegionFlags::READ,
693+
flags,
636694
region_type: MemoryRegionType::Heap,
637695
}
638696
}

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

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,60 @@ 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+
828+
fn exec_mapped_buffer(function_call: &FunctionCall) -> Result<Vec<u8>> {
829+
if let (ParameterValue::ULong(base), ParameterValue::ULong(len)) = (
830+
function_call.parameters.clone().unwrap()[0].clone(),
831+
function_call.parameters.clone().unwrap()[1].clone(),
832+
) {
833+
let base = base as usize as *mut u8;
834+
let len = len as usize;
835+
836+
unsafe {
837+
hyperlight_guest_bin::paging::map_region(base as _, base as _, len as u64 + 4096)
838+
};
839+
840+
let data = unsafe { core::slice::from_raw_parts(base, len) };
841+
842+
// Should be safe as long as data is something like a NOOP followed by a RET
843+
let func: fn() = unsafe { core::mem::transmute(data.as_ptr()) };
844+
func();
845+
846+
Ok(get_flatbuffer_result(true))
847+
} else {
848+
Err(HyperlightGuestError::new(
849+
ErrorCode::GuestFunctionParameterTypeMismatch,
850+
"Invalid parameters passed to read_mapped_buffer".to_string(),
851+
))
852+
}
853+
}
854+
801855
#[no_mangle]
802856
pub extern "C" fn hyperlight_main() {
803857
let read_from_user_memory_def = GuestFunctionDefinition::new(
@@ -818,6 +872,24 @@ pub extern "C" fn hyperlight_main() {
818872

819873
register_function(read_mapped_buffer_def);
820874

875+
let write_mapped_buffer_def = GuestFunctionDefinition::new(
876+
"WriteMappedBuffer".to_string(),
877+
Vec::from(&[ParameterType::ULong, ParameterType::ULong]),
878+
ReturnType::Bool,
879+
write_mapped_buffer as usize,
880+
);
881+
882+
register_function(write_mapped_buffer_def);
883+
884+
let exec_mapped_buffer_def = GuestFunctionDefinition::new(
885+
"ExecMappedBuffer".to_string(),
886+
Vec::from(&[ParameterType::ULong, ParameterType::ULong]),
887+
ReturnType::Bool,
888+
exec_mapped_buffer as usize,
889+
);
890+
891+
register_function(exec_mapped_buffer_def);
892+
821893
let set_static_def = GuestFunctionDefinition::new(
822894
"SetStatic".to_string(),
823895
Vec::new(),

0 commit comments

Comments
 (0)