Skip to content

Commit 3f9d342

Browse files
committed
implement memory region tracking in drivers
- Separate Vecs for initial sandbox regions and mmap regions - Replace unmap_regions(n) with unmap_region(region) for precise control - Add Hash derive for MemoryRegion and related types - Enable use of MemoryRegino in HashSet for efficient set operations Signed-off-by: Ludvig Liljenberg <[email protected]>
1 parent 79a8cdc commit 3f9d342

File tree

5 files changed

+94
-53
lines changed

5 files changed

+94
-53
lines changed

src/hyperlight_host/src/hypervisor/hyperv_linux.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ use super::{
7676
use super::{HyperlightExit, Hypervisor, InterruptHandle, LinuxInterruptHandle, VirtualCPU};
7777
#[cfg(gdb)]
7878
use crate::HyperlightError;
79+
use crate::hypervisor::get_memory_access_violation;
7980
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
8081
use crate::mem::ptr::{GuestPtr, RawPtr};
8182
use crate::sandbox::SandboxConfiguration;
@@ -310,7 +311,8 @@ pub(crate) struct HypervLinuxDriver {
310311
vm_fd: VmFd,
311312
vcpu_fd: VcpuFd,
312313
entrypoint: u64,
313-
mem_regions: Vec<MemoryRegion>,
314+
sandbox_regions: Vec<MemoryRegion>,
315+
mmap_regions: Vec<MemoryRegion>,
314316
orig_rsp: GuestPtr,
315317
interrupt_handle: Arc<LinuxInterruptHandle>,
316318

@@ -443,7 +445,8 @@ impl HypervLinuxDriver {
443445
page_size: 0,
444446
vm_fd,
445447
vcpu_fd,
446-
mem_regions,
448+
sandbox_regions: mem_regions,
449+
mmap_regions: Vec::new(),
447450
entrypoint: entrypoint_ptr.absolute()?,
448451
orig_rsp: rsp_ptr,
449452
interrupt_handle: interrupt_handle.clone(),
@@ -534,8 +537,11 @@ impl Debug for HypervLinuxDriver {
534537
f.field("Entrypoint", &self.entrypoint)
535538
.field("Original RSP", &self.orig_rsp);
536539

537-
for region in &self.mem_regions {
538-
f.field("Memory Region", &region);
540+
for region in &self.sandbox_regions {
541+
f.field("Sandbox Memory Region", &region);
542+
}
543+
for region in &self.mmap_regions {
544+
f.field("Mapped Memory Region", &region);
539545
}
540546

541547
let regs = self.vcpu_fd.get_regs();
@@ -627,20 +633,24 @@ impl Hypervisor for HypervLinuxDriver {
627633
}
628634
let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
629635
self.vm_fd.map_user_memory(mshv_region)?;
630-
self.mem_regions.push(rgn.to_owned());
636+
self.mmap_regions.push(rgn.to_owned());
631637
Ok(())
632638
}
633639

634640
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
635-
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
636-
for rgn in self
637-
.mem_regions
638-
.split_off(self.mem_regions.len() - n as usize)
639-
{
640-
let mshv_region: mshv_user_mem_region = rgn.to_owned().into();
641+
unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
642+
if let Some(pos) = self.mmap_regions.iter().position(|r| r == region) {
643+
let removed_region = self.mmap_regions.remove(pos);
644+
let mshv_region: mshv_user_mem_region = removed_region.into();
641645
self.vm_fd.unmap_user_memory(mshv_region)?;
646+
Ok(())
647+
} else {
648+
Err(new_error!("Tried to unmap region that is not mapped"))
642649
}
643-
Ok(())
650+
}
651+
652+
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
653+
Box::new(self.mmap_regions.iter())
644654
}
645655

646656
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -842,9 +852,9 @@ impl Hypervisor for HypervLinuxDriver {
842852
gpa,
843853
&self
844854
);
845-
match self.get_memory_access_violation(
855+
match get_memory_access_violation(
846856
gpa as usize,
847-
&self.mem_regions,
857+
self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
848858
access_info,
849859
) {
850860
Some(access_info_violation) => access_info_violation,
@@ -974,7 +984,7 @@ impl Hypervisor for HypervLinuxDriver {
974984
});
975985

976986
Ok(Some(crashdump::CrashDumpContext::new(
977-
&self.mem_regions,
987+
&self.sandbox_regions,
978988
regs,
979989
xsave.buffer.to_vec(),
980990
self.entrypoint,
@@ -1147,7 +1157,7 @@ impl Drop for HypervLinuxDriver {
11471157
#[instrument(skip_all, parent = Span::current(), level = "Trace")]
11481158
fn drop(&mut self) {
11491159
self.interrupt_handle.dropped.store(true, Ordering::Relaxed);
1150-
for region in &self.mem_regions {
1160+
for region in self.sandbox_regions.iter().chain(self.mmap_regions.iter()) {
11511161
let mshv_region: mshv_user_mem_region = region.to_owned().into();
11521162
match self.vm_fd.unmap_user_memory(mshv_region) {
11531163
Ok(_) => (),

src/hyperlight_host/src/hypervisor/hyperv_windows.rs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,8 @@ pub(crate) struct HypervWindowsDriver {
279279
_surrogate_process: SurrogateProcess, // we need to keep a reference to the SurrogateProcess for the duration of the driver since otherwise it will dropped and the memory mapping will be unmapped and the surrogate process will be returned to the pool
280280
entrypoint: u64,
281281
orig_rsp: GuestPtr,
282-
mem_regions: Vec<MemoryRegion>,
282+
sandbox_regions: Vec<MemoryRegion>,
283+
mmap_regions: Vec<MemoryRegion>,
283284
interrupt_handle: Arc<WindowsInterruptHandle>,
284285
#[cfg(gdb)]
285286
debug: Option<HypervDebug>,
@@ -355,7 +356,8 @@ impl HypervWindowsDriver {
355356
_surrogate_process: surrogate_process,
356357
entrypoint,
357358
orig_rsp: GuestPtr::try_from(RawPtr::from(rsp))?,
358-
mem_regions,
359+
sandbox_regions: mem_regions,
360+
mmap_regions: Vec::new(),
359361
interrupt_handle: interrupt_handle.clone(),
360362
#[cfg(gdb)]
361363
debug,
@@ -452,8 +454,11 @@ impl Debug for HypervWindowsDriver {
452454
fs.field("Entrypoint", &self.entrypoint)
453455
.field("Original RSP", &self.orig_rsp);
454456

455-
for region in &self.mem_regions {
456-
fs.field("Memory Region", &region);
457+
for region in &self.sandbox_regions {
458+
fs.field("Sandbox Memory Region", &region);
459+
}
460+
for region in &self.mmap_regions {
461+
fs.field("Mapped Memory Region", &region);
457462
}
458463

459464
// Get the registers
@@ -627,18 +632,17 @@ impl Hypervisor for HypervWindowsDriver {
627632
}
628633

629634
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
630-
unsafe fn map_region(&mut self, _rgn: &MemoryRegion) -> Result<()> {
635+
unsafe fn map_region(&mut self, region: &MemoryRegion) -> Result<()> {
631636
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
632637
}
633638

634639
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
635-
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
636-
if n > 0 {
637-
log_then_return!(
638-
"Mapping host memory into the guest not yet supported on this platform"
639-
);
640-
}
641-
Ok(())
640+
unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
641+
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
642+
}
643+
644+
fn get_mapped_regions(&self) -> &[MemoryRegion] {
645+
&self.mmap_regions
642646
}
643647

644648
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -799,8 +803,11 @@ impl Hypervisor for HypervWindowsDriver {
799803
gpa, access_info, &self
800804
);
801805

802-
match self.get_memory_access_violation(gpa as usize, &self.mem_regions, access_info)
803-
{
806+
match self.get_memory_access_violation(
807+
gpa as usize,
808+
self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
809+
access_info,
810+
) {
804811
Some(access_info) => access_info,
805812
None => HyperlightExit::Mmio(gpa),
806813
}
@@ -909,7 +916,7 @@ impl Hypervisor for HypervWindowsDriver {
909916
});
910917

911918
Ok(Some(crashdump::CrashDumpContext::new(
912-
&self.mem_regions,
919+
&self.sandbox_regions,
913920
regs,
914921
xsave,
915922
self.entrypoint,

src/hyperlight_host/src/hypervisor/kvm.rs

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use super::{
4545
use super::{HyperlightExit, Hypervisor, InterruptHandle, LinuxInterruptHandle, VirtualCPU};
4646
#[cfg(gdb)]
4747
use crate::HyperlightError;
48+
use crate::hypervisor::get_memory_access_violation;
4849
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
4950
use crate::mem::ptr::{GuestPtr, RawPtr};
5051
use crate::sandbox::SandboxConfiguration;
@@ -293,7 +294,9 @@ pub(crate) struct KVMDriver {
293294
vcpu_fd: VcpuFd,
294295
entrypoint: u64,
295296
orig_rsp: GuestPtr,
296-
mem_regions: Vec<MemoryRegion>,
297+
sandbox_regions: Vec<MemoryRegion>,
298+
mmap_regions: Vec<(MemoryRegion, u32)>, // (region, slot_number)
299+
next_slot: u32,
297300
interrupt_handle: Arc<LinuxInterruptHandle>,
298301

299302
#[cfg(gdb)]
@@ -382,7 +385,9 @@ impl KVMDriver {
382385
vcpu_fd,
383386
entrypoint,
384387
orig_rsp: rsp_gp,
385-
mem_regions,
388+
next_slot: mem_regions.len() as u32,
389+
sandbox_regions: mem_regions,
390+
mmap_regions: Vec::new(),
386391
interrupt_handle: interrupt_handle.clone(),
387392
#[cfg(gdb)]
388393
debug,
@@ -430,8 +435,11 @@ impl Debug for KVMDriver {
430435
let mut f = f.debug_struct("KVM Driver");
431436
// Output each memory region
432437

433-
for region in &self.mem_regions {
434-
f.field("Memory Region", &region);
438+
for region in &self.sandbox_regions {
439+
f.field("Sandbox Memory Region", &region);
440+
}
441+
for region in &self.mmap_regions {
442+
f.field("Mapped Memory Region", &region);
435443
}
436444
let regs = self.vcpu_fd.get_regs();
437445
// check that regs is OK and then set field in debug struct
@@ -515,25 +523,32 @@ impl Hypervisor for KVMDriver {
515523
}
516524

517525
let mut kvm_region: kvm_userspace_memory_region = region.clone().into();
518-
kvm_region.slot = self.mem_regions.len() as u32;
526+
kvm_region.slot = self.next_slot;
519527
unsafe { self.vm_fd.set_user_memory_region(kvm_region) }?;
520-
self.mem_regions.push(region.to_owned());
528+
self.mmap_regions.push((region.to_owned(), self.next_slot));
529+
self.next_slot += 1;
521530
Ok(())
522531
}
523532

524533
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
525-
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()> {
526-
let n_keep = self.mem_regions.len() - n as usize;
527-
for (k, region) in self.mem_regions.split_off(n_keep).iter().enumerate() {
528-
let mut kvm_region: kvm_userspace_memory_region = region.clone().into();
529-
kvm_region.slot = (n_keep + k) as u32;
534+
unsafe fn unmap_region(&mut self, region: &MemoryRegion) -> Result<()> {
535+
if let Some(idx) = self.mmap_regions.iter().position(|(r, _)| r == region) {
536+
let (region, slot) = self.mmap_regions.remove(idx);
537+
let mut kvm_region: kvm_userspace_memory_region = region.into();
538+
kvm_region.slot = slot;
530539
// Setting memory_size to 0 unmaps the slot's region
531540
// From https://docs.kernel.org/virt/kvm/api.html
532541
// > Deleting a slot is done by passing zero for memory_size.
533542
kvm_region.memory_size = 0;
534543
unsafe { self.vm_fd.set_user_memory_region(kvm_region) }?;
544+
Ok(())
545+
} else {
546+
Err(new_error!("Tried to unmap region that is not mapped"))
535547
}
536-
Ok(())
548+
}
549+
550+
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
551+
Box::new(self.mmap_regions.iter().map(|(region, _)| region))
537552
}
538553

539554
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -694,9 +709,11 @@ impl Hypervisor for KVMDriver {
694709
Ok(VcpuExit::MmioRead(addr, _)) => {
695710
crate::debug!("KVM MMIO Read -Details: Address: {} \n {:#?}", addr, &self);
696711

697-
match self.get_memory_access_violation(
712+
match get_memory_access_violation(
698713
addr as usize,
699-
&self.mem_regions,
714+
self.sandbox_regions
715+
.iter()
716+
.chain(self.mmap_regions.iter().map(|(r, _)| r)),
700717
MemoryRegionFlags::READ,
701718
) {
702719
Some(access_violation_exit) => access_violation_exit,
@@ -706,9 +723,11 @@ impl Hypervisor for KVMDriver {
706723
Ok(VcpuExit::MmioWrite(addr, _)) => {
707724
crate::debug!("KVM MMIO Write -Details: Address: {} \n {:#?}", addr, &self);
708725

709-
match self.get_memory_access_violation(
726+
match get_memory_access_violation(
710727
addr as usize,
711-
&self.mem_regions,
728+
self.sandbox_regions
729+
.iter()
730+
.chain(self.mmap_regions.iter().map(|(r, _)| r)),
712731
MemoryRegionFlags::WRITE,
713732
) {
714733
Some(access_violation_exit) => access_violation_exit,
@@ -824,7 +843,7 @@ impl Hypervisor for KVMDriver {
824843
// The [`CrashDumpContext`] accepts xsave as a vector of u8, so we need to convert the
825844
// xsave region to a vector of u8
826845
Ok(Some(crashdump::CrashDumpContext::new(
827-
&self.mem_regions,
846+
&self.sandbox_regions,
828847
regs,
829848
xsave
830849
.region

src/hyperlight_host/src/hypervisor/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,13 @@ pub(crate) trait Hypervisor: Debug + Sync + Send {
155155
/// requirements of at least one page for base and len.
156156
unsafe fn map_region(&mut self, rgn: &MemoryRegion) -> Result<()>;
157157

158-
/// Unmap the most recent `n` regions mapped by `map_region`
159-
unsafe fn unmap_regions(&mut self, n: u64) -> Result<()>;
158+
/// Unmap a memory region from the sandbox
159+
unsafe fn unmap_region(&mut self, rgn: &MemoryRegion) -> Result<()>;
160+
161+
/// Get the currently mapped dynamic memory regions (not including sandbox regions)
162+
///
163+
/// Note: Box needed for trait to be object-safe :(
164+
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_>;
160165

161166
/// Dispatch a call from the host to the guest using the given pointer
162167
/// to the dispatch function _in the guest's address space_.

src/hyperlight_host/src/mem/memory_region.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub(crate) const DEFAULT_GUEST_BLOB_MEM_FLAGS: MemoryRegionFlags = MemoryRegionF
5252

5353
bitflags! {
5454
/// flags representing memory permission for a memory region
55-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
55+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
5656
pub struct MemoryRegionFlags: u32 {
5757
/// no permissions
5858
const NONE = 0;
@@ -154,7 +154,7 @@ impl TryFrom<hv_x64_memory_intercept_message> for MemoryRegionFlags {
154154
}
155155

156156
// only used for debugging
157-
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
157+
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
158158
/// The type of memory region
159159
pub enum MemoryRegionType {
160160
/// The region contains the guest's page tables
@@ -181,7 +181,7 @@ pub enum MemoryRegionType {
181181

182182
/// represents a single memory region inside the guest. All memory within a region has
183183
/// the same memory permissions
184-
#[derive(Debug, Clone, PartialEq, Eq)]
184+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
185185
pub struct MemoryRegion {
186186
/// the range of guest memory addresses
187187
pub guest_region: Range<usize>,

0 commit comments

Comments
 (0)