diff options
| author | Linfeng Sun <linfeng.sun.dev@gmail.com> | 2026-09-11 17:00:58 +0800 |
|---|---|---|
| committer | Michael S. Tsirkin <mst@redhat.com> | 2026-11-02 09:06:59 -0500 |
| commit | 8517ed4429a2eefd002dce00ea46e23e6714350b (patch) | |
| tree | 3ed368d905110dc3f823249f78832f0dd5f8dc23 /drivers | |
| parent | d7825623a9d4452f97da7ea0d84b2967d507ccf9 (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>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/vdpa/vdpa_sim/vdpa_sim.c | 4 | ||||
| -rw-r--r-- | drivers/vhost/vdpa.c | 3 |
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); } } |
