[PATCH 05/14] vhost: fix VDUSE stale vring pointers after IOTLB invalidation
Maxime Coquelin
maxime.coquelin at redhat.com
Wed Oct 14 17:00:40 CEST 2026
When the VDUSE handler processes VDUSE_UPDATE_IOTLB, it removes the
IOTLB cache entry but fails to invalidate vring pointers that were
derived from that mapping. Since VDUSE also unmaps the memory via
munmap(), the vring pointers (vq->desc, vq->avail, vq->used) become
dangling while vq->access_ok remains true, causing use-after-free.
Add the missing vring invalidation logic to the VDUSE_UPDATE_IOTLB
handler, mirroring what the vhost-user path already does correctly
in VHOST_IOTLB_INVALIDATE handling. Move is_vring_iotlb() to vhost.c
so it can be shared by both vhost_user.c and vduse.c.
CVE-2026-86567
Bugzilla ID: 2029
Cc: stable at dpdk.org
Reported-by: Ivar Al-Safawi <hemo40047 at gmail.com>
Acked-by: Kevin Traynor <ktraynor at redhat.com>
Signed-off-by: Maxime Coquelin <maxime.coquelin at redhat.com>
---
lib/vhost/vduse.c | 24 ++++++++++++-
lib/vhost/vhost.c | 76 ++++++++++++++++++++++++++++++++++++++++++
lib/vhost/vhost.h | 2 ++
lib/vhost/vhost_user.c | 75 -----------------------------------------
4 files changed, 101 insertions(+), 76 deletions(-)
diff --git a/lib/vhost/vduse.c b/lib/vhost/vduse.c
index f8a4a8edcb..58c8f4090b 100644
--- a/lib/vhost/vduse.c
+++ b/lib/vhost/vduse.c
@@ -442,13 +442,35 @@ vduse_events_handler(int fd, void *arg, int *close __rte_unused)
dev->reconnect_log->status = dev->status;
resp.result = VDUSE_REQ_RESULT_OK;
break;
- case VDUSE_UPDATE_IOTLB:
+ case VDUSE_UPDATE_IOTLB: {
+ struct vhost_iotlb_msg imsg;
+ uint32_t i;
+
VHOST_CONFIG_LOG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64,
(uint64_t)req.iova.start, (uint64_t)req.iova.last);
vhost_user_iotlb_cache_remove(dev, req.iova.start,
req.iova.last - req.iova.start + 1);
+
+ /* Invalidate vring translations if affected by this range */
+ imsg.iova = req.iova.start;
+ imsg.size = req.iova.last - req.iova.start + 1;
+
+ for (i = 0; i < dev->nr_vring; i++) {
+ vq = dev->virtqueue[i];
+
+ if (!vq)
+ continue;
+
+ if (is_vring_iotlb(dev, vq, &imsg)) {
+ rte_rwlock_write_lock(&vq->access_lock);
+ vring_invalidate(dev, vq);
+ rte_rwlock_write_unlock(&vq->access_lock);
+ }
+ }
+
resp.result = VDUSE_REQ_RESULT_OK;
break;
+ }
default:
resp.result = VDUSE_REQ_RESULT_FAILED;
break;
diff --git a/lib/vhost/vhost.c b/lib/vhost/vhost.c
index fde8acb00c..2d694d586e 100644
--- a/lib/vhost/vhost.c
+++ b/lib/vhost/vhost.c
@@ -566,6 +566,82 @@ vring_translate(struct virtio_net *dev, struct vhost_virtqueue *vq)
return 0;
}
+static int
+is_vring_iotlb_split(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)
+{
+ struct vhost_vring_addr *ra;
+ uint64_t start, end, len;
+
+ start = imsg->iova;
+ end = start + imsg->size;
+
+ ra = &vq->ring_addrs;
+ len = sizeof(struct vring_desc) * vq->size;
+ if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)
+ return 1;
+
+ len = sizeof(struct vring_avail) + sizeof(uint16_t) * vq->size;
+ if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)
+ return 1;
+
+ len = sizeof(struct vring_used) +
+ sizeof(struct vring_used_elem) * vq->size;
+ if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)
+ return 1;
+
+ if (ra->flags & (1 << VHOST_VRING_F_LOG)) {
+ len = sizeof(uint64_t);
+ if (ra->log_guest_addr < end &&
+ (ra->log_guest_addr + len) > start)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+is_vring_iotlb_packed(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)
+{
+ struct vhost_vring_addr *ra;
+ uint64_t start, end, len;
+
+ start = imsg->iova;
+ end = start + imsg->size;
+
+ ra = &vq->ring_addrs;
+ len = sizeof(struct vring_packed_desc) * vq->size;
+ if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)
+ return 1;
+
+ len = sizeof(struct vring_packed_desc_event);
+ if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)
+ return 1;
+
+ len = sizeof(struct vring_packed_desc_event);
+ if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)
+ return 1;
+
+ if (ra->flags & (1 << VHOST_VRING_F_LOG)) {
+ len = sizeof(uint64_t);
+ if (ra->log_guest_addr < end &&
+ (ra->log_guest_addr + len) > start)
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+is_vring_iotlb(struct virtio_net *dev,
+ struct vhost_virtqueue *vq,
+ struct vhost_iotlb_msg *imsg)
+{
+ if (vq_is_packed(dev))
+ return is_vring_iotlb_packed(vq, imsg);
+ else
+ return is_vring_iotlb_split(vq, imsg);
+}
+
void
vring_invalidate(struct virtio_net *dev __rte_unused, struct vhost_virtqueue *vq)
{
diff --git a/lib/vhost/vhost.h b/lib/vhost/vhost.h
index bb4708aed5..6bed0b6717 100644
--- a/lib/vhost/vhost.h
+++ b/lib/vhost/vhost.h
@@ -909,6 +909,8 @@ uint64_t translate_log_addr(struct virtio_net *dev, struct vhost_virtqueue *vq,
__rte_requires_shared_capability(&vq->iotlb_lock);
void vring_invalidate(struct virtio_net *dev, struct vhost_virtqueue *vq)
__rte_requires_capability(&vq->access_lock);
+int is_vring_iotlb(struct virtio_net *dev, struct vhost_virtqueue *vq,
+ struct vhost_iotlb_msg *imsg);
static __rte_always_inline uint64_t
vhost_iova_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,
diff --git a/lib/vhost/vhost_user.c b/lib/vhost/vhost_user.c
index 018adfe4b2..15f13ef443 100644
--- a/lib/vhost/vhost_user.c
+++ b/lib/vhost/vhost_user.c
@@ -2878,81 +2878,6 @@ vhost_user_set_req_fd(struct virtio_net **pdev,
return RTE_VHOST_MSG_RESULT_OK;
}
-static int
-is_vring_iotlb_split(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)
-{
- struct vhost_vring_addr *ra;
- uint64_t start, end, len;
-
- start = imsg->iova;
- end = start + imsg->size;
-
- ra = &vq->ring_addrs;
- len = sizeof(struct vring_desc) * vq->size;
- if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)
- return 1;
-
- len = sizeof(struct vring_avail) + sizeof(uint16_t) * vq->size;
- if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)
- return 1;
-
- len = sizeof(struct vring_used) +
- sizeof(struct vring_used_elem) * vq->size;
- if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)
- return 1;
-
- if (ra->flags & (1 << VHOST_VRING_F_LOG)) {
- len = sizeof(uint64_t);
- if (ra->log_guest_addr < end &&
- (ra->log_guest_addr + len) > start)
- return 1;
- }
-
- return 0;
-}
-
-static int
-is_vring_iotlb_packed(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)
-{
- struct vhost_vring_addr *ra;
- uint64_t start, end, len;
-
- start = imsg->iova;
- end = start + imsg->size;
-
- ra = &vq->ring_addrs;
- len = sizeof(struct vring_packed_desc) * vq->size;
- if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)
- return 1;
-
- len = sizeof(struct vring_packed_desc_event);
- if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)
- return 1;
-
- len = sizeof(struct vring_packed_desc_event);
- if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)
- return 1;
-
- if (ra->flags & (1 << VHOST_VRING_F_LOG)) {
- len = sizeof(uint64_t);
- if (ra->log_guest_addr < end &&
- (ra->log_guest_addr + len) > start)
- return 1;
- }
-
- return 0;
-}
-
-static int is_vring_iotlb(struct virtio_net *dev,
- struct vhost_virtqueue *vq,
- struct vhost_iotlb_msg *imsg)
-{
- if (vq_is_packed(dev))
- return is_vring_iotlb_packed(vq, imsg);
- else
- return is_vring_iotlb_split(vq, imsg);
-}
-
static int
vhost_user_get_config(struct virtio_net **pdev,
struct vhu_msg_context *ctx,
--
2.56.0
More information about the dev
mailing list