* [PATCH v5] vhost/vdpa: validate virtqueue index in mmap and fault paths
[not found] <20260508063745.90506-1-q.h.hack.winter@gmail.com>
@ 2026-05-08 7:58 ` Qihang Tang
0 siblings, 0 replies; only message in thread
From: Qihang Tang @ 2026-05-08 7:58 UTC (permalink / raw)
To: mst
Cc: jasowang, w, eperezma, Qihang Tang, kvm, linux-kernel, netdev,
virtualization
vhost_vdpa_mmap() and vhost_vdpa_fault() use vma->vm_pgoff as a
virtqueue index for get_vq_notification(), but they do not validate
that the index is smaller than v->nvqs.
The ioctl path already performs both a bounds check and
array_index_nospec(), but the mmap/fault path only checks that the
index fits in u16. This allows an out-of-range queue index to reach
driver-specific get_vq_notification() callbacks.
Fix this by extracting a unified vhost_vdpa_get_vq_notification()
helper that validates the queue index against v->nvqs and applies
array_index_nospec() before calling the driver callback. Both the
mmap and fault paths use this helper, and the bounds checking is
consolidated into a single location.
>From source inspection, the most defensible impact is out-of-bounds
access in the callback path, potentially leading to invalid PFN
remaps and crash/DoS.
Fixes: ddd89d0a059d ("vhost_vdpa: support doorbell mapping via mmap")
Acked-by: Eugenio Pérez <eperezma@redhat.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Qihang Tang <q.h.hack.winter@gmail.com>
---
drivers/vhost/vdpa.c | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c
index 692564b1bcbb..ac55275fa0d0 100644
--- a/drivers/vhost/vdpa.c
+++ b/drivers/vhost/vdpa.c
@@ -1482,16 +1482,32 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep)
}
#ifdef CONFIG_MMU
-static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf)
+static int
+vhost_vdpa_get_vq_notification(struct vhost_vdpa *v, unsigned long index,
+ struct vdpa_notification_area *notify)
{
- struct vhost_vdpa *v = vmf->vma->vm_file->private_data;
struct vdpa_device *vdpa = v->vdpa;
const struct vdpa_config_ops *ops = vdpa->config;
+
+ if (index > 65535 || index >= v->nvqs)
+ return -EINVAL;
+
+ index = array_index_nospec(index, v->nvqs);
+
+ *notify = ops->get_vq_notification(vdpa, index);
+
+ return 0;
+}
+
+static vm_fault_t vhost_vdpa_fault(struct vm_fault *vmf)
+{
+ struct vhost_vdpa *v = vmf->vma->vm_file->private_data;
struct vdpa_notification_area notify;
struct vm_area_struct *vma = vmf->vma;
- u16 index = vma->vm_pgoff;
+ unsigned long index = vma->vm_pgoff;
- notify = ops->get_vq_notification(vdpa, index);
+ if (vhost_vdpa_get_vq_notification(v, index, ¬ify))
+ return VM_FAULT_SIGBUS;
return vmf_insert_pfn(vma, vmf->address & PAGE_MASK, PFN_DOWN(notify.addr));
}
@@ -1514,8 +1530,6 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
return -EINVAL;
if (vma->vm_flags & VM_READ)
return -EINVAL;
- if (index > 65535)
- return -EINVAL;
if (!ops->get_vq_notification)
return -ENOTSUPP;
@@ -1523,7 +1537,8 @@ static int vhost_vdpa_mmap(struct file *file, struct vm_area_struct *vma)
* support the doorbell which sits on the page boundary and
* does not share the page with other registers.
*/
- notify = ops->get_vq_notification(vdpa, index);
+ if (vhost_vdpa_get_vq_notification(v, index, ¬ify))
+ return -EINVAL;
if (notify.addr & (PAGE_SIZE - 1))
return -EINVAL;
if (vma->vm_end - vma->vm_start != notify.size)
--
2.39.5 (Apple Git-154)
^ permalink raw reply related [flat|nested] only message in thread