mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
vhost/vdpa: validate virtqueue index in mmap and fault paths
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: ddd89d0a05 ("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>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Message-ID: <20260508075821.92656-1-q.h.hack.winter@gmail.com>
This commit is contained in:
committed by
Michael S. Tsirkin
parent
e440e07774
commit
929e4f0446
@@ -1482,16 +1482,32 @@ static int vhost_vdpa_release(struct inode *inode, struct file *filep)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
static int
|
||||
vhost_vdpa_get_vq_notification(struct vhost_vdpa *v, unsigned long index,
|
||||
struct vdpa_notification_area *notify)
|
||||
{
|
||||
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_device *vdpa = v->vdpa;
|
||||
const struct vdpa_config_ops *ops = vdpa->config;
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user