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/vhost | |
| 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/vhost')
| -rw-r--r-- | drivers/vhost/vdpa.c | 3 |
1 files changed, 2 insertions, 1 deletions
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); } } |
