summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinfeng Sun <linfeng.sun.dev@gmail.com>2026-09-11 17:00:58 +0800
committerMichael S. Tsirkin <mst@redhat.com>2026-11-02 09:06:59 -0500
commit8517ed4429a2eefd002dce00ea46e23e6714350b (patch)
tree3ed368d905110dc3f823249f78832f0dd5f8dc23
parentd7825623a9d4452f97da7ea0d84b2967d507ccf9 (diff)
vdpa_sim: serialize IOTLB updates with dataplane access
The simulator translates IOTLB entries into iovecs or bvecs under iommu_lock, then drops the lock before copying data. A concurrent DMA unmap can delete the mapping and release its pinned backing pages while the copy is still in progress, leading to a UAF in _copy_from_iter(). Use the existing simulator mutex to serialize DMA map changes with the full backend work function. Also make vhost-vdpa stop device access before unpinning the backing pages. Otherwise, waiting in the simulator DMA unmap callback happens after the pages have already been released and cannot protect their lifetime. Keep iommu_lock for the IOTLB table itself and avoid adding a second long-lived lock to the simulator. I found this bug myself, though the patch was written with AI assistance. Fixes: 2c53d0f64c06 ("vdpasim: vDPA device simulator") Assisted-by: OpenAI-Codex:GPT-5 Signed-off-by: Linfeng Sun <linfeng.sun.dev@gmail.com> Message-ID: <20260911-fix-vdpa-iotlb-lifetime-v3-1-8766d371748e@gmail.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
-rw-r--r--drivers/vdpa/vdpa_sim/vdpa_sim.c4
-rw-r--r--drivers/vhost/vdpa.c3
2 files changed, 6 insertions, 1 deletions
diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c
index cfa88a60a38f..017366cb6684 100644
--- a/drivers/vdpa/vdpa_sim/vdpa_sim.c
+++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c
@@ -713,6 +713,7 @@ static int vdpasim_dma_map(struct vdpa_device *vdpa, unsigned int asid,
if (asid >= vdpasim->dev_attr.nas)
return -EINVAL;
+ mutex_lock(&vdpasim->mutex);
spin_lock(&vdpasim->iommu_lock);
if (vdpasim->iommu_pt[asid]) {
vhost_iotlb_reset(&vdpasim->iommu[asid]);
@@ -721,6 +722,7 @@ static int vdpasim_dma_map(struct vdpa_device *vdpa, unsigned int asid,
ret = vhost_iotlb_add_range_ctx(&vdpasim->iommu[asid], iova,
iova + size - 1, pa, perm, opaque);
spin_unlock(&vdpasim->iommu_lock);
+ mutex_unlock(&vdpasim->mutex);
return ret;
}
@@ -733,6 +735,7 @@ static int vdpasim_dma_unmap(struct vdpa_device *vdpa, unsigned int asid,
if (asid >= vdpasim->dev_attr.nas)
return -EINVAL;
+ mutex_lock(&vdpasim->mutex);
spin_lock(&vdpasim->iommu_lock);
if (vdpasim->iommu_pt[asid]) {
vhost_iotlb_reset(&vdpasim->iommu[asid]);
@@ -740,6 +743,7 @@ static int vdpasim_dma_unmap(struct vdpa_device *vdpa, unsigned int asid,
}
vhost_iotlb_del_range(&vdpasim->iommu[asid], iova, iova + size - 1);
spin_unlock(&vdpasim->iommu_lock);
+ mutex_unlock(&vdpasim->mutex);
return 0;
}
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index ff7d90719171..43678124646a 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -976,6 +976,8 @@ static void vhost_vdpa_pa_unmap(struct vhost_vdpa *v, struct vhost_iotlb *iotlb,
unsigned long pfn, pinned;
while ((map = vhost_iotlb_itree_first(iotlb, start, last)) != NULL) {
+ /* Stop device access before releasing the pinned pages. */
+ vhost_vdpa_general_unmap(v, map, asid);
pinned = PFN_DOWN(map->size);
for (pfn = PFN_DOWN(map->addr);
pinned > 0; pfn++, pinned--) {
@@ -985,7 +987,6 @@ static void vhost_vdpa_pa_unmap(struct vhost_vdpa *v, struct vhost_iotlb *iotlb,
unpin_user_page(page);
}
atomic64_sub(PFN_DOWN(map->size), &dev->mm->pinned_vm);
- vhost_vdpa_general_unmap(v, map, asid);
vhost_iotlb_map_free(iotlb, map);
}
}