Re: [PATCH] vhost: fix used ring index wrap in vhost_add_used_n_in_order
From: Eugenio Perez Martin
Date: Wed Oct 21 2026 - 03:30:19 EST
On Wed, Oct 21, 2026 at 1:30 AM Fang Xieyan <fangxy@xxxxxxxxxxxx> wrote:
>
> vhost_add_used_n_in_order() advances start by nheads[i] but wraps it
> with a single subtraction. If start + nheads[i] >= 2 * vq->num, start
> can remain outside the ring.
>
> With used-ring logging enabled, start > vq->num makes (vq->num - start)
> underflow. log_write() then sets bits beyond the dirty-log bitmap,
> potentially corrupting adjacent userspace memory. This is reachable on
> the vhost-net TX path with VIRTIO_F_IN_ORDER: done_idx can reach
> VHOST_NET_BATCH (64), triggering the issue with a 32-entry ring.
>
> Wrap start with a mask to keep it within the ring. VHOST_SET_VRING_NUM
> enforces a nonzero power-of-two ring size, allowing start to be wrapped
> using & (vq->num - 1).
>
> Fixes: 67a873df0c41 ("vhost: basic in order support")
> Cc: stable@xxxxxxxxxxxxxxx
> Reviewed-by: Weibin Liu <liuwb@xxxxxxxxxxxx>
> Signed-off-by: Fang Xieyan <fangxy@xxxxxxxxxxxx>
> ---
> Reproducer (verified on a kernel containing commit 67a873df0c41 with a
> temporary WARN_ON_ONCE probe in log_used(); the probe is not part of
> this patch):
>
> With VIRTIO_F_IN_ORDER, num=32, and used-ring dirty logging enabled, the
> TX batch reaches done_idx=64.
Can you send the reproducer? I'm very curious about how vhost_net
reaches to done_idx=64. It seems some bound needs to be added there
too, isn't it?
The patch itself looks good to me, even if we will need to modify it
for packed vqs, so
Acked-by: Eugenio Pérez <eperezma@xxxxxxxxxx>
> In the observed run, the initial index is
> 3, so the single subtraction leaves start=35. The resulting length
> calculation wraps to 34359738344 bytes, as confirmed by the log_used()
> probe.
>
> A 64-byte canary immediately after the 512-byte log bitmap changes from
> 0xA5 to 0xFF, confirming the out-of-bounds write. The used-ring canary
> remains intact because vhost_put_used() runs before start is advanced;
> the demonstrated corruption is in the dirty-log bitmap.
>
> drivers/vhost/vhost.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
> index 44cac11b68d2..0226c4c59709 100644
> --- a/drivers/vhost/vhost.c
> +++ b/drivers/vhost/vhost.c
> @@ -3172,8 +3172,7 @@ static int vhost_add_used_n_in_order(struct vhost_virtqueue *vq,
> }
> start += nheads[i];
> new += nheads[i];
> - if (start >= vq->num)
> - start -= vq->num;
> + start &= vq->num - 1;
> }
>
> if (unlikely(vq->log_used)) {
> --
> 2.50.1
>