From: Jia Jia <physicalmtea@gmail.com>
To: mst@redhat.com, jasowang@redhat.com
Cc: kvm@vger.kernel.org, virtualization@lists.linux.dev,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v3] vhost/vdpa: reject VRING_NUM larger than device max
Date: Mon, 10 Aug 2026 09:03:00 +0800 [thread overview]
Message-ID: <20260810010300.132959-1-physicalmtea@gmail.com> (raw)
vhost_vring_set_num() accepts any non-zero power-of-two queue size that
fits in 16 bits. vhost-vdpa then passes that value to set_vq_num()
without comparing it with get_vq_num_max().
A process with access to /dev/vhost-vdpa-* can therefore configure a
queue larger than the device advertises. With vdpa_sim, the worker can
walk descriptors beyond the mapped descriptor ring. KASAN reports a
16-byte out-of-bounds read, corresponding to one vring_desc, in the
vringh IOTLB path:
BUG: KASAN: out-of-bounds in _copy_from_iter
Read of size 16
copy_from_iotlb
copydesc_iotlb
vringh_getdesc_iotlb
vdpasim_net_work
Cache get_vq_num_max() immediately after reset. Some backends derive
it from writable queue-size state, so querying it after SET_NUM may
return the current size instead of the device capability. Invalidate
the cached value before reset so a failed reset leaves SET_NUM
disabled.
For VHOST_SET_VRING_NUM, copy the complete vring state once and use
the same index and size for validation, vq->num, and set_vq_num().
This ensures that validation and use operate on the same copied values.
Fixes: 4c8cf31885f6 ("vhost: introduce vDPA-based backend")
Signed-off-by: Jia Jia <physicalmtea@gmail.com>
---
v3:
- Copy VHOST_SET_VRING_NUM once and use the copied values throughout,
avoiding the double-fetch race in v2.
- Cache the device-wide maximum after reset so mutable queue state
cannot break a later legal resize; fail closed if reset fails.
v2:
- Add the missing Signed-off-by tag.
drivers/vhost/vdpa.c | 44 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 37 insertions(+), 7 deletions(-)
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index 692564b1bcbb..f2eedb0464c2 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -53,6 +53,7 @@ struct vhost_vdpa {
struct cdev cdev;
atomic_t opened;
u32 nvqs;
+ u16 vq_num_max;
int virtio_id;
int minor;
struct eventfd_ctx *config_ctx;
@@ -229,7 +230,9 @@ static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid)
static int _compat_vdpa_reset(struct vhost_vdpa *v)
{
struct vdpa_device *vdpa = v->vdpa;
+ const struct vdpa_config_ops *ops = vdpa->config;
u32 flags = 0;
+ int ret;
v->suspended = false;
@@ -239,7 +242,14 @@ static int _compat_vdpa_reset(struct vhost_vdpa *v)
VDPA_RESET_F_CLEAN_MAP : 0;
}
- return vdpa_reset(vdpa, flags);
+ v->vq_num_max = 0;
+ ret = vdpa_reset(vdpa, flags);
+ if (!ret) {
+ /* Some backends derive the max from mutable queue state. */
+ v->vq_num_max = ops->get_vq_num_max(vdpa);
+ }
+
+ return ret;
}
static int vhost_vdpa_reset(struct vhost_vdpa *v)
@@ -641,9 +651,15 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
u32 idx;
long r;
- r = get_user(idx, (u32 __user *)argp);
- if (r < 0)
- return r;
+ if (cmd == VHOST_SET_VRING_NUM) {
+ if (copy_from_user(&s, argp, sizeof(s)))
+ return -EFAULT;
+ idx = s.index;
+ } else {
+ r = get_user(idx, (u32 __user *)argp);
+ if (r < 0)
+ return r;
+ }
if (idx >= v->nvqs)
return -ENOBUFS;
@@ -652,6 +668,23 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
vq = &v->vqs[idx];
switch (cmd) {
+ case VHOST_SET_VRING_NUM:
+ mutex_lock(&vq->mutex);
+ if (vq->private_data) {
+ r = -EBUSY;
+ } else if (!s.num || s.num > 0xffff ||
+ s.num > v->vq_num_max ||
+ (s.num & (s.num - 1))) {
+ r = -EINVAL;
+ } else {
+ vq->num = s.num;
+ r = 0;
+ }
+ mutex_unlock(&vq->mutex);
+ if (r)
+ return r;
+ ops->set_vq_num(vdpa, idx, s.num);
+ return 0;
case VHOST_VDPA_SET_VRING_ENABLE:
if (copy_from_user(&s, argp, sizeof(s)))
return -EFAULT;
@@ -765,9 +798,6 @@ static long vhost_vdpa_vring_ioctl(struct vhost_vdpa *v, unsigned int cmd,
ops->set_vq_cb(vdpa, idx, &cb);
break;
- case VHOST_SET_VRING_NUM:
- ops->set_vq_num(vdpa, idx, vq->num);
- break;
}
return r;
--
2.34.1
reply other threads:[~2026-08-10 1:03 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260810010300.132959-1-physicalmtea@gmail.com \
--to=physicalmtea@gmail.com \
--cc=jasowang@redhat.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mst@redhat.com \
--cc=netdev@vger.kernel.org \
--cc=virtualization@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.