[PATCH OLK-6.6 0/3] CVE-2026-97994
CVE-2026-97994 Jia Jia (1): vhost/vdpa: reject VRING_NUM larger than device max Si-Wei Liu (2): vdpa: introduce .compat_reset operation callback vhost-vdpa: clean iotlb map during reset for older userspace drivers/vhost/vdpa.c | 62 ++++++++++++++++++++++++++++++------ drivers/virtio/virtio_vdpa.c | 2 +- include/linux/vdpa.h | 20 ++++++++++-- 3 files changed, 71 insertions(+), 13 deletions(-) -- 2.34.1
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/30868 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/W2O... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/30868 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/W2O...
From: Si-Wei Liu <si-wei.liu@oracle.com> stable inclusion from stable-v6.6.158 commit b46594205234a8cfff47dc9d1196698cc342d77d category: feature bugzilla: https://atomgit.com/src-openeuler/kernel/issues/19814 CVE: CVE-2026-97994 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit a26f2e4e68ee3130e5d5acb4f58807041aaea905 ] Some device specific IOMMU parent drivers have long standing bogus behaviour that mistakenly clean up the maps during .reset. By definition, this is violation to the on-chip IOMMU ops (i.e. .set_map, or .dma_map & .dma_unmap) in those offending drivers, as the removal of internal maps is completely agnostic to the upper layer, causing inconsistent view between the userspace and the kernel. Some userspace app like QEMU gets around of this brokenness by proactively removing and adding back all the maps around vdpa device reset, but such workaround actually penaltize other well-behaved driver setup, where vdpa reset always comes with the associated mapping cost, especially for kernel vDPA devices (use_va=false) that have high cost on pinning. It's imperative to rectify this behaviour and remove the problematic code from all those non-compliant parent drivers. However, we cannot unconditionally remove the bogus map-cleaning code from the buggy .reset implementation, as there might exist userspace apps that already rely on the behaviour on some setup. Introduce a .compat_reset driver op to keep compatibility with older userspace. New and well behaved parent driver should not bother to implement such op, but only those drivers that are doing or used to do non-compliant map-cleaning reset will have to. Signed-off-by: Si-Wei Liu <si-wei.liu@oracle.com> Message-Id: <1697880319-4937-5-git-send-email-si-wei.liu@oracle.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Lei Yang <leiyang@redhat.com> Stable-dep-of: ccb1dc7c527f ("vhost/vdpa: reject VRING_NUM larger than device max") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Jiacheng Yu <yujiacheng3@huawei.com> Conflicts: include/linux/vdpa.h [1. Context conflicts: this tree carries a private reset-state extension (enum vdpa_reset_state, reset op taking @state), so the reset op keeps its @state argument next to the newly added compat_reset op.] Signed-off-by: Jiacheng Yu <yujiacheng3@huawei.com> --- include/linux/vdpa.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index 09482250192c..32528aae815a 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -258,6 +258,17 @@ enum vdpa_reset_state { * VDPA_DEV_RESET_OPEN for vhost-vdpa device open * VDPA_DEV_RESET_CLOSE for vhost-vdpa device close * Returns integer: success (0) or error (< 0) + * @compat_reset: Reset device with compatibility quirks to + * accommodate older userspace. Only needed by + * parent driver which used to have bogus reset + * behaviour, and has to maintain such behaviour + * for compatibility with older userspace. + * Historically compliant driver only has to + * implement .reset, Historically non-compliant + * driver should implement both. + * @vdev: vdpa device + * @flags: compatibility quirks for reset + * Returns integer: success (0) or error (< 0) * @suspend: Suspend the device (optional) * @vdev: vdpa device * Returns integer: success (0) or error (< 0) @@ -424,6 +435,8 @@ struct vdpa_config_ops { u8 (*get_status)(struct vdpa_device *vdev); void (*set_status)(struct vdpa_device *vdev, u8 status); int (*reset)(struct vdpa_device *vdev, int state); + int (*compat_reset)(struct vdpa_device *vdev, u32 flags); +#define VDPA_RESET_F_CLEAN_MAP 1 int (*suspend)(struct vdpa_device *vdev); int (*resume)(struct vdpa_device *vdev); size_t (*get_config_size)(struct vdpa_device *vdev); -- 2.34.1
From: Si-Wei Liu <si-wei.liu@oracle.com> stable inclusion from stable-v6.6.158 commit bd94e2ba56d0236fd199604551aa15a55b6df6f3 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/19814 CVE: CVE-2026-97994 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit bc91df5c70ac720eca18bd1f4a288f2582713d3e ] Using .compat_reset op from the previous patch, the buggy .reset behaviour can be kept as-is on older userspace apps, which don't ack the IOTLB_PERSIST backend feature. As this compatibility quirk is limited to those drivers that used to be buggy in the past, it won't affect change the behaviour or affect ABI on the setups with API compliant driver. The separation of .compat_reset from the regular .reset allows vhost-vdpa able to know which driver had broken behaviour before, so it can apply the corresponding compatibility quirk to the individual driver whenever needed. Compared to overloading the existing .reset with flags, .compat_reset won't cause any extra burden to the implementation of every compliant driver. [mst: squashed in two fixup commits] Message-Id: <1697880319-4937-6-git-send-email-si-wei.liu@oracle.com> Message-Id: <1698102863-21122-1-git-send-email-si-wei.liu@oracle.com> Reported-by: Dragos Tatulea <dtatulea@nvidia.com> Tested-by: Dragos Tatulea <dtatulea@nvidia.com> Message-Id: <1698275594-19204-1-git-send-email-si-wei.liu@oracle.com> Reported-by: Lei Yang <leiyang@redhat.com> Signed-off-by: Si-Wei Liu <si-wei.liu@oracle.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Tested-by: Lei Yang <leiyang@redhat.com> Stable-dep-of: ccb1dc7c527f ("vhost/vdpa: reject VRING_NUM larger than device max") Signed-off-by: Sasha Levin <sashal@kernel.org> Conflicts: drivers/vhost/vdpa.c drivers/virtio/virtio_vdpa.c include/linux/vdpa.h [1. Context conflicts. 2. This tree carries a private reset-state extension (enum vdpa_reset_state, reset op taking @state). The upstream compat reset flags and the local reset state share the same parameter slot, so vdpa_reset() and _compat_vdpa_reset() are extended to take both: the upstream @flags come first, the local @state is kept as the last argument. compat_reset() itself only receives @flags, and no driver in this tree implements it, so ops->compat_reset is always NULL and the reset op keeps receiving @state exactly as before.] Signed-off-by: Jiacheng Yu <yujiacheng3@huawei.com> --- drivers/vhost/vdpa.c | 20 ++++++++++++++++---- drivers/virtio/virtio_vdpa.c | 2 +- include/linux/vdpa.h | 7 +++++-- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index 2fffd45830d4..c652b26d65fc 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -221,13 +221,24 @@ static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid) irq_bypass_unregister_producer(&vq->call_ctx.producer); } -static int vhost_vdpa_reset(struct vhost_vdpa *v, int state) +static int _compat_vdpa_reset(struct vhost_vdpa *v, int state) { struct vdpa_device *vdpa = v->vdpa; + u32 flags = 0; - v->in_batch = 0; + if (v->vdev.vqs) { + flags |= !vhost_backend_has_feature(v->vdev.vqs[0], + VHOST_BACKEND_F_IOTLB_PERSIST) ? + VDPA_RESET_F_CLEAN_MAP : 0; + } + + return vdpa_reset(vdpa, flags, state); +} - return vdpa_reset(vdpa, state); +static int vhost_vdpa_reset(struct vhost_vdpa *v, int state) +{ + v->in_batch = 0; + return _compat_vdpa_reset(v, state); } static long vhost_vdpa_bind_mm(struct vhost_vdpa *v) @@ -306,7 +317,7 @@ static long vhost_vdpa_set_status(struct vhost_vdpa *v, u8 __user *statusp) vhost_vdpa_unsetup_vq_irq(v, i); if (status == 0) { - ret = vdpa_reset(vdpa, VDPA_DEV_RESET_VIRTIO); + ret = _compat_vdpa_reset(v, VDPA_DEV_RESET_VIRTIO); if (ret) return ret; } else @@ -1658,6 +1669,7 @@ static void vhost_vdpa_cleanup(struct vhost_vdpa *v) vhost_vdpa_free_domain(v); vhost_dev_cleanup(&v->vdev); kfree(v->vdev.vqs); + v->vdev.vqs = NULL; } static int vhost_vdpa_open(struct inode *inode, struct file *filep) diff --git a/drivers/virtio/virtio_vdpa.c b/drivers/virtio/virtio_vdpa.c index 5e2b1bb4d009..0d767b9a16e8 100644 --- a/drivers/virtio/virtio_vdpa.c +++ b/drivers/virtio/virtio_vdpa.c @@ -100,7 +100,7 @@ static void virtio_vdpa_reset(struct virtio_device *vdev) { struct vdpa_device *vdpa = vd_get_vdpa(vdev); - vdpa_reset(vdpa, VDPA_DEV_RESET_VIRTIO); + vdpa_reset(vdpa, 0, VDPA_DEV_RESET_VIRTIO); } static bool virtio_vdpa_notify(struct virtqueue *vq) diff --git a/include/linux/vdpa.h b/include/linux/vdpa.h index 32528aae815a..216186f27474 100644 --- a/include/linux/vdpa.h +++ b/include/linux/vdpa.h @@ -565,14 +565,17 @@ static inline struct device *vdpa_get_dma_dev(struct vdpa_device *vdev) return vdev->dma_dev; } -static inline int vdpa_reset(struct vdpa_device *vdev, int state) +static inline int vdpa_reset(struct vdpa_device *vdev, u32 flags, int state) { const struct vdpa_config_ops *ops = vdev->config; int ret; down_write(&vdev->cf_lock); vdev->features_valid = false; - ret = ops->reset(vdev, state); + if (ops->compat_reset && flags) + ret = ops->compat_reset(vdev, flags); + else + ret = ops->reset(vdev, state); up_write(&vdev->cf_lock); return ret; } -- 2.34.1
From: Jia Jia <physicalmtea@gmail.com> stable inclusion from stable-v6.6.158 commit 4875c65ca53797a0a402fe2bb54d1b12f28b3cda category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/19814 CVE: CVE-2026-97994 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit ccb1dc7c527f8c925925cf92afc76ae590dac311 ] 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> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20260810010300.132959-1-physicalmtea@gmail.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Conflicts: drivers/vhost/vdpa.c [1. Context conflicts: vdpa_reset() takes the local reset @state argument as the last parameter, so the call in _compat_vdpa_reset() passes both @flags and @state.] Signed-off-by: Jiacheng Yu <yujiacheng3@huawei.com> --- 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 c652b26d65fc..99a16fa5ff76 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -62,6 +62,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; @@ -224,7 +225,9 @@ static void vhost_vdpa_unsetup_vq_irq(struct vhost_vdpa *v, u16 qid) static int _compat_vdpa_reset(struct vhost_vdpa *v, int state) { struct vdpa_device *vdpa = v->vdpa; + const struct vdpa_config_ops *ops = vdpa->config; u32 flags = 0; + int ret; if (v->vdev.vqs) { flags |= !vhost_backend_has_feature(v->vdev.vqs[0], @@ -232,7 +235,14 @@ static int _compat_vdpa_reset(struct vhost_vdpa *v, int state) VDPA_RESET_F_CLEAN_MAP : 0; } - return vdpa_reset(vdpa, flags, state); + v->vq_num_max = 0; + ret = vdpa_reset(vdpa, flags, state); + 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, int state) @@ -844,9 +854,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; @@ -855,6 +871,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; @@ -942,9 +975,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
participants (2)
-
Jiacheng Yu -
patchwork bot