From 825999933c3bb051d4f6c848a0227c5370c79017 Mon Sep 17 00:00:00 2001 From: Solicey Date: Sun, 28 Jun 2026 23:31:22 +0800 Subject: [PATCH 1/2] add memory overlap check --- src/arch/aarch64/zone.rs | 11 +++++++++++ src/arch/loongarch64/zone.rs | 37 ++++++++++++++++++++++++++++++------ src/arch/riscv64/zone.rs | 11 +++++++++++ src/arch/x86_64/zone.rs | 11 +++++++++++ src/memory/mm.rs | 24 +++++++++++++++++++++++ src/memory/mmio.rs | 7 +++++++ src/zone.rs | 30 ++++++++++++++++++++++++++++- 7 files changed, 124 insertions(+), 7 deletions(-) diff --git a/src/arch/aarch64/zone.rs b/src/arch/aarch64/zone.rs index 15883ba8..cfdff7ab 100644 --- a/src/arch/aarch64/zone.rs +++ b/src/arch/aarch64/zone.rs @@ -36,6 +36,17 @@ impl Zone { } match mem_region.mem_type { MEM_TYPE_RAM | MEM_TYPE_IO => { + // Check for overlap with registered MMIO handler regions. + if inner.is_mmio_handler_overlap( + mem_region.virtual_start as GuestPhysAddr, + mem_region.size as _, + ) { + panic!( + "Passthrough region [{:#x}, {:#x}) overlaps with existing MMIO handler", + mem_region.virtual_start, + mem_region.virtual_start as u64 + mem_region.size + ); + } inner .gpm_mut() .insert(MemoryRegion::new_with_offset_mapper( diff --git a/src/arch/loongarch64/zone.rs b/src/arch/loongarch64/zone.rs index baa50d13..76b65389 100644 --- a/src/arch/loongarch64/zone.rs +++ b/src/arch/loongarch64/zone.rs @@ -47,6 +47,17 @@ impl Zone { let mem_type = region.mem_type; match mem_type { MEM_TYPE_RAM => { + // Check for overlap with registered MMIO handler regions. + if inner.is_mmio_handler_overlap( + region.virtual_start as GuestPhysAddr, + region.size as _, + ) { + panic!( + "Passthrough region [{:#x}, {:#x}) overlaps with existing MMIO handler", + region.virtual_start, + region.virtual_start as u64 + region.size + ); + } inner .gpm_mut() .insert(MemoryRegion::new_with_offset_mapper( @@ -57,6 +68,17 @@ impl Zone { ))?; } MEM_TYPE_IO => { + // Check for overlap with registered MMIO handler regions. + if inner.is_mmio_handler_overlap( + region.virtual_start as GuestPhysAddr, + region.size as _, + ) { + panic!( + "Passthrough region [{:#x}, {:#x}) overlaps with existing MMIO handler", + region.virtual_start, + region.virtual_start as u64 + region.size + ); + } inner .gpm_mut() .insert(MemoryRegion::new_with_offset_mapper( @@ -71,6 +93,15 @@ impl Zone { "loongarch64: pt_init: register virtio mmio region: {:#x?}", region ); + // Register the mmio handler first, so that the overlap check + // against the stage-2 page table in mmio_region_register does + // not find the VIRTIO trap page we are about to insert. + inner.mmio_region_register( + region.physical_start as _, + region.size as _, + mmio_virtio_handler, + region.physical_start as _, + ); inner .gpm_mut() .insert(MemoryRegion::new_with_offset_mapper( @@ -79,12 +110,6 @@ impl Zone { PAGE_SIZE, // since we only need 0x200 size for virtio mmio, but the minimal size is PAGE_SIZE MemFlags::USER, // we use the USER as a hint flag for invalidating this stage-2 PTE ))?; - inner.mmio_region_register( - region.physical_start as _, - region.size as _, - mmio_virtio_handler, - region.physical_start as _, - ); } _ => { error!("loongarch64: pt_init: unknown mem type: {}", mem_type); diff --git a/src/arch/riscv64/zone.rs b/src/arch/riscv64/zone.rs index d11e7a5b..cdc559cc 100644 --- a/src/arch/riscv64/zone.rs +++ b/src/arch/riscv64/zone.rs @@ -32,6 +32,17 @@ impl Zone { } match mem_region.mem_type { MEM_TYPE_RAM | MEM_TYPE_IO => { + // Check for overlap with registered MMIO handler regions. + if inner.is_mmio_handler_overlap( + mem_region.virtual_start as GuestPhysAddr, + mem_region.size as _, + ) { + panic!( + "Passthrough region [{:#x}, {:#x}) overlaps with existing MMIO handler", + mem_region.virtual_start, + mem_region.virtual_start as u64 + mem_region.size + ); + } inner .gpm_mut() .insert(MemoryRegion::new_with_offset_mapper( diff --git a/src/arch/x86_64/zone.rs b/src/arch/x86_64/zone.rs index d671fe51..5493992f 100644 --- a/src/arch/x86_64/zone.rs +++ b/src/arch/x86_64/zone.rs @@ -72,6 +72,17 @@ impl Zone { } match mem_region.mem_type { MEM_TYPE_RAM | MEM_TYPE_IO | MEM_TYPE_RESERVED => { + // Check for overlap with registered MMIO handler regions. + if inner.is_mmio_handler_overlap( + mem_region.virtual_start as GuestPhysAddr, + mem_region.size as _, + ) { + panic!( + "Passthrough region [{:#x}, {:#x}) overlaps with existing MMIO handler", + mem_region.virtual_start, + mem_region.virtual_start as u64 + mem_region.size + ); + } inner.gpm_mut().insert(MemoryRegion::new_with_offset_mapper( mem_region.virtual_start as GuestPhysAddr, mem_region.physical_start as HostPhysAddr, diff --git a/src/memory/mm.rs b/src/memory/mm.rs index dd8fc3ba..d67cbc3f 100644 --- a/src/memory/mm.rs +++ b/src/memory/mm.rs @@ -93,6 +93,30 @@ where self.pt.root_paddr() } + /// Check whether `[start, start+size)` overlaps with any existing region in this MemorySet. + pub fn is_range_overlap(&self, start: usize, size: usize) -> bool + where + PT::VA: From, + { + let end = start + size; + let va_start: PT::VA = start.into(); + // Check the region immediately before `start`. + if let Some((_, before)) = self.regions.range(..va_start).last() { + let before_end: usize = before.start.into() + before.size; + if before_end > start { + return true; + } + } + // Check the region at or after `start`. + if let Some((_, after)) = self.regions.range(va_start..).next() { + let after_start: usize = after.start.into(); + if after_start < end { + return true; + } + } + false + } + fn test_free_area(&self, other: &MemoryRegion) -> bool { if let Some((_, before)) = self.regions.range(..other.start).last() { if before.is_overlap_with(other) { diff --git a/src/memory/mmio.rs b/src/memory/mmio.rs index 1d477189..f8522da1 100644 --- a/src/memory/mmio.rs +++ b/src/memory/mmio.rs @@ -51,6 +51,13 @@ impl MMIORegion { pub fn contains_region(&self, addr: GuestPhysAddr, sz: usize) -> bool { addr >= self.start && addr + (sz as usize) <= self.start + (self.size as usize) } + + /// Check whether this region overlaps with `other`. + pub fn is_overlap_with(&self, other: &MMIORegion) -> bool { + let self_end = self.start + self.size; + let other_end = other.start + other.size; + !(self_end <= other.start || self.start >= other_end) + } } pub fn mmio_perform_access(base: usize, mmio: &mut MMIOAccess) { diff --git a/src/zone.rs b/src/zone.rs index b01553f0..e586811e 100644 --- a/src/zone.rs +++ b/src/zone.rs @@ -223,8 +223,30 @@ impl ZoneInner { mmio.handler = handler; mmio.arg = arg; } else { + let new_region = MMIORegion { start, size }; + + // Check for overlap with existing mmio handler regions. + if let Some(existing) = self + .mmio + .iter() + .find(|cfg| cfg.region.is_overlap_with(&new_region)) + { + panic!( + "New MMIO handler region {:#x?} overlaps with existing handler {:#x?}", + new_region, existing.region + ); + } + + // Check for overlap with passthrough regions in the stage-2 page table. + if self.gpm.is_range_overlap(start, size) { + panic!( + "New MMIO handler region {:#x?} overlaps with passthrough region in stage-2 page table", + new_region + ); + } + self.mmio.push(MMIOConfig { - region: MMIORegion { start, size }, + region: new_region, handler, arg, }) @@ -253,6 +275,12 @@ impl ZoneInner { .find(|cfg| cfg.region.contains_region(addr, size)) .map(|cfg| (cfg.region, cfg.handler, cfg.arg)) } + + /// Check whether `[start, start+size)` overlaps with any registered MMIO handler region. + pub fn is_mmio_handler_overlap(&self, start: GuestPhysAddr, size: usize) -> bool { + let region = MMIORegion { start, size }; + self.mmio.iter().any(|cfg| cfg.region.is_overlap_with(®ion)) + } /// If irq_id belongs to this zone pub fn irq_in_zone(&self, irq_id: u32) -> bool { let idx = (irq_id / 32) as usize; From bde1fb61f6f0144202a4b29618cbd66140248dfe Mon Sep 17 00:00:00 2001 From: Solicey Date: Sun, 28 Jun 2026 23:38:35 +0800 Subject: [PATCH 2/2] fix format --- src/zone.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/zone.rs b/src/zone.rs index e586811e..53331cf9 100644 --- a/src/zone.rs +++ b/src/zone.rs @@ -279,7 +279,9 @@ impl ZoneInner { /// Check whether `[start, start+size)` overlaps with any registered MMIO handler region. pub fn is_mmio_handler_overlap(&self, start: GuestPhysAddr, size: usize) -> bool { let region = MMIORegion { start, size }; - self.mmio.iter().any(|cfg| cfg.region.is_overlap_with(®ion)) + self.mmio + .iter() + .any(|cfg| cfg.region.is_overlap_with(®ion)) } /// If irq_id belongs to this zone pub fn irq_in_zone(&self, irq_id: u32) -> bool {