virtio-devices: iommu: Enforce input_range on MAP requests

The device offers VIRTIO_IOMMU_F_INPUT_RANGE when the address
width is constrained, but never validates that guest MAP requests
fall inside the advertised range. The virtio spec requires such
requests to fail with VIRTIO_IOMMU_S_RANGE.

Signed-off-by: Rob Bradford <rbradford@meta.com>
Assisted-by: Claude:claude-opus-4-7
This commit is contained in:
Rob Bradford
2026-04-26 22:15:03 +01:00
parent 1376f6e9ef
commit efcc090811

View File

@@ -347,6 +347,7 @@ impl Request {
mapping: &Arc<IommuMapping>,
ext_mapping: &BTreeMap<u32, Arc<dyn ExternalDmaMapping>>,
msi_iova_space: (u64, u64),
input_range: Option<(u64, u64)>,
) -> result::Result<usize, Error> {
let desc = desc_chain
.next()
@@ -490,6 +491,13 @@ impl Request {
return Err(Error::InvalidMapRequest);
}
if let Some((lo, hi)) = input_range
&& (req.virt_start < lo || req.virt_end > hi)
{
status = VIRTIO_IOMMU_S_RANGE;
return Err(Error::InvalidMapRequest);
}
// Copy the value to use it as a proper reference.
let domain_id = req.domain;
@@ -611,6 +619,13 @@ impl Request {
.map_err(Error::GuestMemory)?;
debug!("Unmap request 0x{req:x?}");
if let Some((lo, hi)) = input_range
&& (req.virt_start < lo || req.virt_end > hi)
{
status = VIRTIO_IOMMU_S_RANGE;
return Err(Error::InvalidUnmapRequest);
}
// Copy the value to use it as a proper reference.
let domain_id = req.domain;
let virt_start = req.virt_start;
@@ -808,6 +823,7 @@ struct IommuEpollHandler {
mapping: Arc<IommuMapping>,
ext_mapping: Arc<Mutex<BTreeMap<u32, Arc<dyn ExternalDmaMapping>>>>,
msi_iova_space: (u64, u64),
input_range: Option<(u64, u64)>,
}
impl IommuEpollHandler {
@@ -820,6 +836,7 @@ impl IommuEpollHandler {
&self.mapping,
&self.ext_mapping.lock().unwrap(),
self.msi_iova_space,
self.input_range,
)?;
self.request_queue
@@ -1032,6 +1049,7 @@ pub struct Iommu {
seccomp_action: SeccompAction,
exit_evt: EventFd,
msi_iova_space: (u64, u64),
input_range: Option<(u64, u64)>,
}
type EndpointsState = Vec<(u32, u32)>;
@@ -1091,13 +1109,14 @@ impl Iommu {
..Default::default()
};
if address_width_bits < 64 {
let input_range = if address_width_bits < 64 {
avail_features |= 1u64 << VIRTIO_IOMMU_F_INPUT_RANGE;
config.input_range = VirtioIommuRange64 {
start: 0,
end: (1u64 << address_width_bits) - 1,
}
}
let end = (1u64 << address_width_bits) - 1;
config.input_range = VirtioIommuRange64 { start: 0, end };
Some((0, end))
} else {
None
};
let mapping = Arc::new(IommuMapping {
endpoints: Arc::new(RwLock::new(endpoints)),
@@ -1124,6 +1143,7 @@ impl Iommu {
seccomp_action,
exit_evt,
msi_iova_space,
input_range,
},
mapping,
))
@@ -1251,6 +1271,7 @@ impl VirtioDevice for Iommu {
mapping: self.mapping.clone(),
ext_mapping: self.ext_mapping.clone(),
msi_iova_space: self.msi_iova_space,
input_range: self.input_range,
};
let paused = self.common.paused.clone();