[PATCH 23.11 05/14] vhost: fix VDUSE stale vring pointers after IOTLB invalidation

Don Salvatore Nero hemo40047 at gmail.com
Wed Oct 14 22:55:12 CEST 2026


Dear Maxime,

I hope you are doing well.

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.

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.

I would like to thank you for:

The clarity of your technical analysis and CVSS assessment

Your careful handling

The quality of the patches

Your patience throughout the process, especially given the extended
timelines.

I look forward to continuing to contribute to DPDK security by discovering
many more vulnerabilities in the future, and in any way I can.

Thank you once again for your time, your expertise, and your continued work
on DPDK.

I am committed to the embargo period until the vulnerabilities are
disclosed by you.

With sincere appreciation,
Ivar Al-Safawi

في الأربعاء، ١٤ تشرين الأول ٢٠٢٦، ٦:٠٢ م Maxime Coquelin <
maxime.coquelin at redhat.com> كتب:

> 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
>
> Reported-by: Ivar Al-Safawi <hemo40047 at gmail.com>
> Signed-off-by: Maxime Coquelin <maxime.coquelin at redhat.com>
> Acked-by: Kevin Traynor <ktraynor at redhat.com>
> ---
>  lib/vhost/vduse.c      | 26 +++++++++++++--
>  lib/vhost/vhost.c      | 76 ++++++++++++++++++++++++++++++++++++++++++
>  lib/vhost/vhost.h      |  2 ++
>  lib/vhost/vhost_user.c | 75 -----------------------------------------
>  4 files changed, 102 insertions(+), 77 deletions(-)
>
> diff --git a/lib/vhost/vduse.c b/lib/vhost/vduse.c
> index 3d8d3dcb31..bd1c1df88e 100644
> --- a/lib/vhost/vduse.c
> +++ b/lib/vhost/vduse.c
> @@ -439,13 +439,35 @@ vduse_events_handler(int fd, void *arg, int *remove
> __rte_unused)
>                 dev->status = req.s.status;
>                 resp.result = VDUSE_REQ_RESULT_OK;
>                 break;
> -       case VDUSE_UPDATE_IOTLB:
> -               VHOST_LOG_CONFIG(dev->ifname, INFO, "\tIOVA range: %"
> PRIx64 " - %" PRIx64 "\n",
> +       case VDUSE_UPDATE_IOTLB: {
> +               struct vhost_iotlb_msg imsg;
> +               uint32_t i;
> +
> +               VHOST_LOG_CONFIG(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 29d2d4d205..3f4dea4822 100644
> --- a/lib/vhost/vhost.c
> +++ b/lib/vhost/vhost.c
> @@ -580,6 +580,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 17a9ba66ca..bcf86669c1 100644
> --- a/lib/vhost/vhost.h
> +++ b/lib/vhost/vhost.h
> @@ -881,6 +881,8 @@ uint64_t translate_log_addr(struct virtio_net *dev,
> struct vhost_virtqueue *vq,
>                 uint64_t log_addr)
>         __rte_shared_locks_required(&vq->iotlb_lock);
>  void vring_invalidate(struct virtio_net *dev, struct vhost_virtqueue *vq);
> +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 9b19de952a..4505381cc4 100644
> --- a/lib/vhost/vhost_user.c
> +++ b/lib/vhost/vhost_user.c
> @@ -2495,81 +2495,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
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mails.dpdk.org/archives/stable/attachments/20261014/6c7b0f7e/attachment.htm>


More information about the stable mailing list