<div dir="auto">Dear Maxime,<div dir="auto"><br></div><div dir="auto">I hope you are doing well.</div><div dir="auto"><br></div><div dir="auto">I am writing to sincerely thank you and the DPDK security team for your work on the four vulnerabilities I previously reported this year — CVE-2026-86561, CVE-2026-86562, CVE-2026-86563, and CVE-2026-86567.</div><div dir="auto"><br></div><div dir="auto">I fully understand that managing a project the size of DPDK is demanding, and that vulnerability reports add significant pressure on top of your regular work. The care, precision, and professionalism with which you handled each report — from initial triage, through patch development, and all the way to the coordinated disclosure process — was exceptional.</div><div dir="auto"><br></div><div dir="auto">I would like to thank you for:</div><div dir="auto"><br></div><div dir="auto">The clarity of your technical analysis and CVSS assessment</div><div dir="auto"><br></div><div dir="auto">Your careful handling</div><div dir="auto"><br></div><div dir="auto">The quality of the patches</div><div dir="auto"><br></div><div dir="auto">Your patience throughout the process, especially given the extended timelines.</div><div dir="auto"><br></div><div dir="auto">I look forward to continuing to contribute to DPDK security by discovering many more vulnerabilities in the future, and in any way I can.</div><div dir="auto"><br></div><div dir="auto">Thank you once again for your time, your expertise, and your continued work on DPDK.</div><div dir="auto"><br></div><div dir="auto">I am committed to the embargo period until the vulnerabilities are disclosed by you.</div><div dir="auto"><br></div><div dir="auto">With sincere appreciation,</div><div dir="auto">Ivar Al-Safawi</div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">في الأربعاء، ١٤ تشرين الأول ٢٠٢٦، ٦:٠٢ م Maxime Coquelin <<a href="mailto:maxime.coquelin@redhat.com">maxime.coquelin@redhat.com</a>> كتب:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">When the VDUSE handler processes VDUSE_UPDATE_IOTLB, it removes the<br>
IOTLB cache entry but fails to invalidate vring pointers that were<br>
derived from that mapping. Since VDUSE also unmaps the memory via<br>
munmap(), the vring pointers (vq->desc, vq->avail, vq->used) become<br>
dangling while vq->access_ok remains true, causing use-after-free.<br>
<br>
Add the missing vring invalidation logic to the VDUSE_UPDATE_IOTLB<br>
handler, mirroring what the vhost-user path already does correctly<br>
in VHOST_IOTLB_INVALIDATE handling. Move is_vring_iotlb() to vhost.c<br>
so it can be shared by both vhost_user.c and vduse.c.<br>
<br>
CVE-2026-86567<br>
Bugzilla ID: 2029<br>
<br>
Reported-by: Ivar Al-Safawi <<a href="mailto:hemo40047@gmail.com" target="_blank" rel="noreferrer">hemo40047@gmail.com</a>><br>
Signed-off-by: Maxime Coquelin <<a href="mailto:maxime.coquelin@redhat.com" target="_blank" rel="noreferrer">maxime.coquelin@redhat.com</a>><br>
Acked-by: Kevin Traynor <<a href="mailto:ktraynor@redhat.com" target="_blank" rel="noreferrer">ktraynor@redhat.com</a>><br>
---<br>
lib/vhost/vduse.c | 26 +++++++++++++--<br>
lib/vhost/vhost.c | 76 ++++++++++++++++++++++++++++++++++++++++++<br>
lib/vhost/vhost.h | 2 ++<br>
lib/vhost/vhost_user.c | 75 -----------------------------------------<br>
4 files changed, 102 insertions(+), 77 deletions(-)<br>
<br>
diff --git a/lib/vhost/vduse.c b/lib/vhost/vduse.c<br>
index 3d8d3dcb31..bd1c1df88e 100644<br>
--- a/lib/vhost/vduse.c<br>
+++ b/lib/vhost/vduse.c<br>
@@ -439,13 +439,35 @@ vduse_events_handler(int fd, void *arg, int *remove __rte_unused)<br>
dev->status = req.s.status;<br>
resp.result = VDUSE_REQ_RESULT_OK;<br>
break;<br>
- case VDUSE_UPDATE_IOTLB:<br>
- VHOST_LOG_CONFIG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64 "\n",<br>
+ case VDUSE_UPDATE_IOTLB: {<br>
+ struct vhost_iotlb_msg imsg;<br>
+ uint32_t i;<br>
+<br>
+ VHOST_LOG_CONFIG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64,<br>
(uint64_t)req.iova.start, (uint64_t)req.iova.last);<br>
vhost_user_iotlb_cache_remove(dev, req.iova.start,<br>
req.iova.last - req.iova.start + 1);<br>
+<br>
+ /* Invalidate vring translations if affected by this range */<br>
+ imsg.iova = req.iova.start;<br>
+ imsg.size = req.iova.last - req.iova.start + 1;<br>
+<br>
+ for (i = 0; i < dev->nr_vring; i++) {<br>
+ vq = dev->virtqueue[i];<br>
+<br>
+ if (!vq)<br>
+ continue;<br>
+<br>
+ if (is_vring_iotlb(dev, vq, &imsg)) {<br>
+ rte_rwlock_write_lock(&vq->access_lock);<br>
+ vring_invalidate(dev, vq);<br>
+ rte_rwlock_write_unlock(&vq->access_lock);<br>
+ }<br>
+ }<br>
+<br>
resp.result = VDUSE_REQ_RESULT_OK;<br>
break;<br>
+ }<br>
default:<br>
resp.result = VDUSE_REQ_RESULT_FAILED;<br>
break;<br>
diff --git a/lib/vhost/vhost.c b/lib/vhost/vhost.c<br>
index 29d2d4d205..3f4dea4822 100644<br>
--- a/lib/vhost/vhost.c<br>
+++ b/lib/vhost/vhost.c<br>
@@ -580,6 +580,82 @@ vring_translate(struct virtio_net *dev, struct vhost_virtqueue *vq)<br>
return 0;<br>
}<br>
<br>
+static int<br>
+is_vring_iotlb_split(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)<br>
+{<br>
+ struct vhost_vring_addr *ra;<br>
+ uint64_t start, end, len;<br>
+<br>
+ start = imsg->iova;<br>
+ end = start + imsg->size;<br>
+<br>
+ ra = &vq->ring_addrs;<br>
+ len = sizeof(struct vring_desc) * vq->size;<br>
+ if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)<br>
+ return 1;<br>
+<br>
+ len = sizeof(struct vring_avail) + sizeof(uint16_t) * vq->size;<br>
+ if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)<br>
+ return 1;<br>
+<br>
+ len = sizeof(struct vring_used) +<br>
+ sizeof(struct vring_used_elem) * vq->size;<br>
+ if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)<br>
+ return 1;<br>
+<br>
+ if (ra->flags & (1 << VHOST_VRING_F_LOG)) {<br>
+ len = sizeof(uint64_t);<br>
+ if (ra->log_guest_addr < end &&<br>
+ (ra->log_guest_addr + len) > start)<br>
+ return 1;<br>
+ }<br>
+<br>
+ return 0;<br>
+}<br>
+<br>
+static int<br>
+is_vring_iotlb_packed(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)<br>
+{<br>
+ struct vhost_vring_addr *ra;<br>
+ uint64_t start, end, len;<br>
+<br>
+ start = imsg->iova;<br>
+ end = start + imsg->size;<br>
+<br>
+ ra = &vq->ring_addrs;<br>
+ len = sizeof(struct vring_packed_desc) * vq->size;<br>
+ if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)<br>
+ return 1;<br>
+<br>
+ len = sizeof(struct vring_packed_desc_event);<br>
+ if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)<br>
+ return 1;<br>
+<br>
+ len = sizeof(struct vring_packed_desc_event);<br>
+ if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)<br>
+ return 1;<br>
+<br>
+ if (ra->flags & (1 << VHOST_VRING_F_LOG)) {<br>
+ len = sizeof(uint64_t);<br>
+ if (ra->log_guest_addr < end &&<br>
+ (ra->log_guest_addr + len) > start)<br>
+ return 1;<br>
+ }<br>
+<br>
+ return 0;<br>
+}<br>
+<br>
+int<br>
+is_vring_iotlb(struct virtio_net *dev,<br>
+ struct vhost_virtqueue *vq,<br>
+ struct vhost_iotlb_msg *imsg)<br>
+{<br>
+ if (vq_is_packed(dev))<br>
+ return is_vring_iotlb_packed(vq, imsg);<br>
+ else<br>
+ return is_vring_iotlb_split(vq, imsg);<br>
+}<br>
+<br>
void<br>
vring_invalidate(struct virtio_net *dev __rte_unused, struct vhost_virtqueue *vq)<br>
{<br>
diff --git a/lib/vhost/vhost.h b/lib/vhost/vhost.h<br>
index 17a9ba66ca..bcf86669c1 100644<br>
--- a/lib/vhost/vhost.h<br>
+++ b/lib/vhost/vhost.h<br>
@@ -881,6 +881,8 @@ uint64_t translate_log_addr(struct virtio_net *dev, struct vhost_virtqueue *vq,<br>
uint64_t log_addr)<br>
__rte_shared_locks_required(&vq->iotlb_lock);<br>
void vring_invalidate(struct virtio_net *dev, struct vhost_virtqueue *vq);<br>
+int is_vring_iotlb(struct virtio_net *dev, struct vhost_virtqueue *vq,<br>
+ struct vhost_iotlb_msg *imsg);<br>
<br>
static __rte_always_inline uint64_t<br>
vhost_iova_to_vva(struct virtio_net *dev, struct vhost_virtqueue *vq,<br>
diff --git a/lib/vhost/vhost_user.c b/lib/vhost/vhost_user.c<br>
index 9b19de952a..4505381cc4 100644<br>
--- a/lib/vhost/vhost_user.c<br>
+++ b/lib/vhost/vhost_user.c<br>
@@ -2495,81 +2495,6 @@ vhost_user_set_req_fd(struct virtio_net **pdev,<br>
return RTE_VHOST_MSG_RESULT_OK;<br>
}<br>
<br>
-static int<br>
-is_vring_iotlb_split(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)<br>
-{<br>
- struct vhost_vring_addr *ra;<br>
- uint64_t start, end, len;<br>
-<br>
- start = imsg->iova;<br>
- end = start + imsg->size;<br>
-<br>
- ra = &vq->ring_addrs;<br>
- len = sizeof(struct vring_desc) * vq->size;<br>
- if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)<br>
- return 1;<br>
-<br>
- len = sizeof(struct vring_avail) + sizeof(uint16_t) * vq->size;<br>
- if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)<br>
- return 1;<br>
-<br>
- len = sizeof(struct vring_used) +<br>
- sizeof(struct vring_used_elem) * vq->size;<br>
- if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)<br>
- return 1;<br>
-<br>
- if (ra->flags & (1 << VHOST_VRING_F_LOG)) {<br>
- len = sizeof(uint64_t);<br>
- if (ra->log_guest_addr < end &&<br>
- (ra->log_guest_addr + len) > start)<br>
- return 1;<br>
- }<br>
-<br>
- return 0;<br>
-}<br>
-<br>
-static int<br>
-is_vring_iotlb_packed(struct vhost_virtqueue *vq, struct vhost_iotlb_msg *imsg)<br>
-{<br>
- struct vhost_vring_addr *ra;<br>
- uint64_t start, end, len;<br>
-<br>
- start = imsg->iova;<br>
- end = start + imsg->size;<br>
-<br>
- ra = &vq->ring_addrs;<br>
- len = sizeof(struct vring_packed_desc) * vq->size;<br>
- if (ra->desc_user_addr < end && (ra->desc_user_addr + len) > start)<br>
- return 1;<br>
-<br>
- len = sizeof(struct vring_packed_desc_event);<br>
- if (ra->avail_user_addr < end && (ra->avail_user_addr + len) > start)<br>
- return 1;<br>
-<br>
- len = sizeof(struct vring_packed_desc_event);<br>
- if (ra->used_user_addr < end && (ra->used_user_addr + len) > start)<br>
- return 1;<br>
-<br>
- if (ra->flags & (1 << VHOST_VRING_F_LOG)) {<br>
- len = sizeof(uint64_t);<br>
- if (ra->log_guest_addr < end &&<br>
- (ra->log_guest_addr + len) > start)<br>
- return 1;<br>
- }<br>
-<br>
- return 0;<br>
-}<br>
-<br>
-static int is_vring_iotlb(struct virtio_net *dev,<br>
- struct vhost_virtqueue *vq,<br>
- struct vhost_iotlb_msg *imsg)<br>
-{<br>
- if (vq_is_packed(dev))<br>
- return is_vring_iotlb_packed(vq, imsg);<br>
- else<br>
- return is_vring_iotlb_split(vq, imsg);<br>
-}<br>
-<br>
static int<br>
vhost_user_get_config(struct virtio_net **pdev,<br>
struct vhu_msg_context *ctx,<br>
-- <br>
2.56.0<br>
<br>
</blockquote></div>