diff --git a/virtio-devices/src/iommu.rs b/virtio-devices/src/iommu.rs index efb071273..895b70800 100644 --- a/virtio-devices/src/iommu.rs +++ b/virtio-devices/src/iommu.rs @@ -72,6 +72,7 @@ const VIRTIO_IOMMU_F_BYPASS_CONFIG: u32 = 6; // Support 2MiB and 4KiB page sizes. const VIRTIO_IOMMU_PAGE_SIZE_MASK: u64 = (2 << 20) | (4 << 10); +const VIRTIO_IOMMU_PAGE_GRANULE: u64 = 1u64 << VIRTIO_IOMMU_PAGE_SIZE_MASK.trailing_zeros(); // ~64 MiB at ~64 bytes/entry, well above any legitimate workload. const MAX_MAPPINGS_PER_DOMAIN: usize = 1 << 20; @@ -125,11 +126,8 @@ const VIRTIO_IOMMU_S_IOERR: u8 = 1; #[allow(unused)] const VIRTIO_IOMMU_S_UNSUPP: u8 = 2; const VIRTIO_IOMMU_S_DEVERR: u8 = 3; -#[allow(unused)] const VIRTIO_IOMMU_S_INVAL: u8 = 4; -#[allow(unused)] const VIRTIO_IOMMU_S_RANGE: u8 = 5; -#[allow(unused)] const VIRTIO_IOMMU_S_NOENT: u8 = 6; #[allow(unused)] const VIRTIO_IOMMU_S_FAULT: u8 = 7; @@ -165,13 +163,9 @@ struct VirtioIommuReqDetach { } /// Virtio IOMMU request MAP flags -#[allow(unused)] const VIRTIO_IOMMU_MAP_F_READ: u32 = 1; -#[allow(unused)] const VIRTIO_IOMMU_MAP_F_WRITE: u32 = 1 << 1; -#[allow(unused)] const VIRTIO_IOMMU_MAP_F_MMIO: u32 = 1 << 2; -#[allow(unused)] const VIRTIO_IOMMU_MAP_F_MASK: u32 = VIRTIO_IOMMU_MAP_F_READ | VIRTIO_IOMMU_MAP_F_WRITE | VIRTIO_IOMMU_MAP_F_MMIO; @@ -183,7 +177,7 @@ struct VirtioIommuReqMap { virt_start: u64, virt_end: u64, phys_start: u64, - _flags: u32, + flags: u32, } /// UNMAP request @@ -491,6 +485,11 @@ impl Request { .map_err(Error::GuestMemory)?; debug!("Map request 0x{req:x?}"); + if (req.flags & !VIRTIO_IOMMU_MAP_F_MASK) != 0 { + status = VIRTIO_IOMMU_S_INVAL; + return Err(Error::InvalidMapRequest); + } + // Copy the value to use it as a proper reference. let domain_id = req.domain; @@ -500,7 +499,7 @@ impl Request { return Err(Error::InvalidMapRequestBypassDomain); } } else { - status = VIRTIO_IOMMU_S_INVAL; + status = VIRTIO_IOMMU_S_NOENT; return Err(Error::InvalidMapRequestMissingDomain); } @@ -528,6 +527,27 @@ impl Request { return Err(Error::InvalidMapRequest); } + let mask = VIRTIO_IOMMU_PAGE_GRANULE - 1; + if (req.virt_start & mask) != 0 + || (req.phys_start & mask) != 0 + || (size & mask) != 0 + { + status = VIRTIO_IOMMU_S_RANGE; + return Err(Error::InvalidMapRequest); + } + + // Going forward MAP rejects overlap, so within a domain + // mappings are disjoint and the rightmost mapping with + // start <= virt_end is the only candidate to overlap. + if let Some(d) = mapping.domains.read().unwrap().get(&domain_id) + && let Some((&start, m)) = d.mappings.range(..=req.virt_end).next_back() + && let Some(end) = inclusive_end(start, m.size) + && end >= req.virt_start + { + status = VIRTIO_IOMMU_S_INVAL; + return Err(Error::InvalidMapRequest); + } + { let domains = mapping.domains.read().unwrap(); if let Some(d) = domains.get(&domain_id)