Skip to content

Commit d2f4b98

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 d2f4b98

File tree

5 files changed

+95
-53
lines changed

5 files changed

+95
-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: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ use super::{
5959
};
6060
use super::{HyperlightExit, Hypervisor, InterruptHandle, VirtualCPU};
6161
use crate::hypervisor::fpu::FP_CONTROL_WORD_DEFAULT;
62+
use crate::hypervisor::get_memory_access_violation;
6263
use crate::hypervisor::wrappers::WHvGeneralRegisters;
6364
use crate::mem::memory_region::{MemoryRegion, MemoryRegionFlags};
6465
use crate::mem::ptr::{GuestPtr, RawPtr};
@@ -279,7 +280,8 @@ pub(crate) struct HypervWindowsDriver {
279280
_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
280281
entrypoint: u64,
281282
orig_rsp: GuestPtr,
282-
mem_regions: Vec<MemoryRegion>,
283+
sandbox_regions: Vec<MemoryRegion>,
284+
mmap_regions: Vec<MemoryRegion>,
283285
interrupt_handle: Arc<WindowsInterruptHandle>,
284286
#[cfg(gdb)]
285287
debug: Option<HypervDebug>,
@@ -355,7 +357,8 @@ impl HypervWindowsDriver {
355357
_surrogate_process: surrogate_process,
356358
entrypoint,
357359
orig_rsp: GuestPtr::try_from(RawPtr::from(rsp))?,
358-
mem_regions,
360+
sandbox_regions: mem_regions,
361+
mmap_regions: Vec::new(),
359362
interrupt_handle: interrupt_handle.clone(),
360363
#[cfg(gdb)]
361364
debug,
@@ -452,8 +455,11 @@ impl Debug for HypervWindowsDriver {
452455
fs.field("Entrypoint", &self.entrypoint)
453456
.field("Original RSP", &self.orig_rsp);
454457

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

459465
// Get the registers
@@ -627,18 +633,17 @@ impl Hypervisor for HypervWindowsDriver {
627633
}
628634

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

634640
#[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(())
641+
unsafe fn unmap_region(&mut self, _region: &MemoryRegion) -> Result<()> {
642+
log_then_return!("Mapping host memory into the guest not yet supported on this platform");
643+
}
644+
645+
fn get_mapped_regions(&self) -> Box<dyn ExactSizeIterator<Item = &MemoryRegion> + '_> {
646+
Box::new(self.mmap_regions.iter())
642647
}
643648

644649
#[instrument(err(Debug), skip_all, parent = Span::current(), level = "Trace")]
@@ -799,8 +804,11 @@ impl Hypervisor for HypervWindowsDriver {
799804
gpa, access_info, &self
800805
);
801806

802-
match self.get_memory_access_violation(gpa as usize, &self.mem_regions, access_info)
803-
{
807+
match get_memory_access_violation(
808+
gpa as usize,
809+
self.sandbox_regions.iter().chain(self.mmap_regions.iter()),
810+
access_info,
811+
) {
804812
Some(access_info) => access_info,
805813
None => HyperlightExit::Mmio(gpa),
806814
}
@@ -909,7 +917,7 @@ impl Hypervisor for HypervWindowsDriver {
909917
});
910918

911919
Ok(Some(crashdump::CrashDumpContext::new(
912-
&self.mem_regions,
920+
&self.sandbox_regions,
913921
regs,
914922
xsave,
915923
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)