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,
|
InvalidUnmapRequestBypassDomain,
|
||||||
#[error("Invalid to unmap because the domain is missing")]
|
#[error("Invalid to unmap because the domain is missing")]
|
||||||
InvalidUnmapRequestMissingDomain,
|
InvalidUnmapRequestMissingDomain,
|
||||||
|
#[error("UNMAP range partially overlaps an existing mapping")]
|
||||||
|
InvalidUnmapRequestPartialOverlap,
|
||||||
#[error("Guest sent us invalid PROBE request")]
|
#[error("Guest sent us invalid PROBE request")]
|
||||||
InvalidProbeRequest,
|
InvalidProbeRequest,
|
||||||
#[error("Failed to performing external mapping")]
|
#[error("Failed to performing external mapping")]
|
||||||
@@ -561,6 +563,38 @@ impl Request {
|
|||||||
return Err(Error::InvalidUnmapRequestMissingDomain);
|
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.
|
// Find the list of endpoints attached to the given domain.
|
||||||
let endpoints: Vec<u32> = mapping
|
let endpoints: Vec<u32> = mapping
|
||||||
.endpoints
|
.endpoints
|
||||||
@@ -571,15 +605,6 @@ impl Request {
|
|||||||
.map(|(&e, _)| e)
|
.map(|(&e, _)| e)
|
||||||
.collect();
|
.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.
|
// Trigger external unmapping if necessary.
|
||||||
for endpoint in endpoints {
|
for endpoint in endpoints {
|
||||||
if let Some(ext_map) = ext_mapping.get(&endpoint) {
|
if let Some(ext_map) = ext_mapping.get(&endpoint) {
|
||||||
|
|||||||
Reference in New Issue
Block a user