mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
virtio-devices: iommu: Reject UNMAP that partially overlaps a mapping
An UNMAP that would split an existing mapping must be rejected with VIRTIO_IOMMU_S_RANGE without removing anything. The previous start-only retain silently left mappings that started outside the unmap range but overlapped it. Walk bookkeeping under a read lock and reject before touching VFIO so a rejection cannot leave VFIO and bookkeeping out of sync. Signed-off-by: Rob Bradford <rbradford@meta.com> Assisted-by: Claude:claude-opus-4-7
This commit is contained in:
@@ -322,6 +322,8 @@ enum Error {
|
||||
InvalidUnmapRequestBypassDomain,
|
||||
#[error("Invalid to unmap because the domain is missing")]
|
||||
InvalidUnmapRequestMissingDomain,
|
||||
#[error("UNMAP range partially overlaps an existing mapping")]
|
||||
InvalidUnmapRequestPartialOverlap,
|
||||
#[error("Guest sent us invalid PROBE request")]
|
||||
InvalidProbeRequest,
|
||||
#[error("Failed to performing external mapping")]
|
||||
@@ -561,6 +563,38 @@ impl Request {
|
||||
return Err(Error::InvalidUnmapRequestMissingDomain);
|
||||
}
|
||||
|
||||
let Some(size) = req
|
||||
.virt_end
|
||||
.checked_sub(virt_start)
|
||||
.and_then(|d| d.checked_add(1))
|
||||
else {
|
||||
status = VIRTIO_IOMMU_S_RANGE;
|
||||
return Err(Error::InvalidUnmapRequest);
|
||||
};
|
||||
|
||||
// An UNMAP that would split an existing mapping must be
|
||||
// rejected with VIRTIO_IOMMU_S_RANGE without removing
|
||||
// anything. Inspect bookkeeping before touching VFIO so
|
||||
// a rejection cannot leave VFIO out of sync.
|
||||
{
|
||||
let domains = mapping.domains.read().unwrap();
|
||||
let Some(domain) = domains.get(&domain_id) else {
|
||||
status = VIRTIO_IOMMU_S_INVAL;
|
||||
return Err(Error::InvalidUnmapRequestMissingDomain);
|
||||
};
|
||||
for (&start, m) in domain.mappings.iter() {
|
||||
let Some(end) = inclusive_end(start, m.size) else {
|
||||
continue;
|
||||
};
|
||||
let overlaps = start <= req.virt_end && end >= req.virt_start;
|
||||
let split = start < req.virt_start || end > req.virt_end;
|
||||
if overlaps && split {
|
||||
status = VIRTIO_IOMMU_S_RANGE;
|
||||
return Err(Error::InvalidUnmapRequestPartialOverlap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find the list of endpoints attached to the given domain.
|
||||
let endpoints: Vec<u32> = mapping
|
||||
.endpoints
|
||||
@@ -571,15 +605,6 @@ impl Request {
|
||||
.map(|(&e, _)| e)
|
||||
.collect();
|
||||
|
||||
let Some(size) = req
|
||||
.virt_end
|
||||
.checked_sub(virt_start)
|
||||
.and_then(|d| d.checked_add(1))
|
||||
else {
|
||||
status = VIRTIO_IOMMU_S_RANGE;
|
||||
return Err(Error::InvalidUnmapRequest);
|
||||
};
|
||||
|
||||
// Trigger external unmapping if necessary.
|
||||
for endpoint in endpoints {
|
||||
if let Some(ext_map) = ext_mapping.get(&endpoint) {
|
||||
|
||||
Reference in New Issue
Block a user