mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
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: 2c53d0f64c ("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>
This commit is contained in:
parent
d7825623a9
commit
8517ed4429
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user