]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Aug 2026 12:19:59 +0000 (14:19 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 5 Aug 2026 12:19:59 +0000 (14:19 +0200)
added patches:
drm-vmwgfx-avoid-destroy_workqueue-null-on-vkms-init-failure.patch
drm-vmwgfx-bound-dma-command-body-size-against-suffix-pointer.patch
drm-vmwgfx-clamp-dirty-page-range-with-min-not-max.patch
drm-vmwgfx-drop-dma_buf-reference-on-foreign-fd-prime-import.patch
drm-vmwgfx-enforce-cursor-size-limits-for-mob-cursors.patch
drm-vmwgfx-fix-guest_memory_dirty-bitfield-clobbered-as-size.patch
drm-vmwgfx-reject-dx_bind_query-without-a-dx-context.patch
drm-vmwgfx-take-fman-lock-around-fence-list-mutation-in-fifo_down.patch
drm-vmwgfx-use-check_add_overflow-for-shader-size-offset-bound.patch
drm-vmwgfx-validate-draw_primitives-header-size-before-division.patch
drm-vmwgfx-validate-external-bo-copy-bounds-for-both-stride-paths.patch

12 files changed:
queue-6.18/drm-vmwgfx-avoid-destroy_workqueue-null-on-vkms-init-failure.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-bound-dma-command-body-size-against-suffix-pointer.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-clamp-dirty-page-range-with-min-not-max.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-drop-dma_buf-reference-on-foreign-fd-prime-import.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-enforce-cursor-size-limits-for-mob-cursors.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-fix-guest_memory_dirty-bitfield-clobbered-as-size.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-reject-dx_bind_query-without-a-dx-context.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-take-fman-lock-around-fence-list-mutation-in-fifo_down.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-use-check_add_overflow-for-shader-size-offset-bound.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-validate-draw_primitives-header-size-before-division.patch [new file with mode: 0644]
queue-6.18/drm-vmwgfx-validate-external-bo-copy-bounds-for-both-stride-paths.patch [new file with mode: 0644]
queue-6.18/series

diff --git a/queue-6.18/drm-vmwgfx-avoid-destroy_workqueue-null-on-vkms-init-failure.patch b/queue-6.18/drm-vmwgfx-avoid-destroy_workqueue-null-on-vkms-init-failure.patch
new file mode 100644 (file)
index 0000000..8559b10
--- /dev/null
@@ -0,0 +1,74 @@
+From 05eaa887e7b4f40fba425f8a1d7a5a8a043092a6 Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:29 -0400
+Subject: drm/vmwgfx: avoid destroy_workqueue(NULL) on vkms init failure
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit 05eaa887e7b4f40fba425f8a1d7a5a8a043092a6 upstream.
+
+Two paths through vmw_vkms_init() can leave vmw->crc_workq NULL while
+still leaving the rest of the driver in a state that calls
+vmw_vkms_cleanup() at module unload:
+
+  1. vmw_host_get_guestinfo(GUESTINFO_VBLANK, ...) failing or
+     returning an oversized buffer -- the common case on hosts
+     without a VBLANK guestinfo entry -- early-returned before the
+     workqueue allocation.
+  2. alloc_ordered_workqueue() returning NULL on memory pressure.
+
+vmw_vkms_cleanup() then calls destroy_workqueue(NULL), which
+dereferences wq->name and panics.
+
+Fix the first case by removing the early return: vmw->vkms_enabled
+is already false on the rpci-failure path so no work will ever be
+queued, and allocating the workqueue unconditionally keeps the
+control flow simple.  Fix the second case by guarding the cleanup
+with a NULL check, since alloc_ordered_workqueue() can still fail
+under low memory.
+
+Fixes: 7b0062036c3b ("drm/vmwgfx: Implement virtual crc generation")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-9-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c |   17 +++++++++--------
+ 1 file changed, 9 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c
+@@ -214,14 +214,14 @@ vmw_vkms_init(struct vmw_private *vmw)
+       vmw->vkms_enabled = false;
+       ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len);
+-      if (ret || buf_len > max_buf_len)
+-              return;
+-      buffer[buf_len] = '\0';
++      if (!ret && buf_len <= max_buf_len) {
++              buffer[buf_len] = '\0';
+-      ret = kstrtobool(buffer, &vmw->vkms_enabled);
+-      if (!ret && vmw->vkms_enabled) {
+-              ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
+-              vmw->vkms_enabled = (ret == 0);
++              ret = kstrtobool(buffer, &vmw->vkms_enabled);
++              if (!ret && vmw->vkms_enabled) {
++                      ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS);
++                      vmw->vkms_enabled = (ret == 0);
++              }
+       }
+       vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0);
+@@ -236,7 +236,8 @@ vmw_vkms_init(struct vmw_private *vmw)
+ void
+ vmw_vkms_cleanup(struct vmw_private *vmw)
+ {
+-      destroy_workqueue(vmw->crc_workq);
++      if (vmw->crc_workq)
++              destroy_workqueue(vmw->crc_workq);
+ }
+ bool
diff --git a/queue-6.18/drm-vmwgfx-bound-dma-command-body-size-against-suffix-pointer.patch b/queue-6.18/drm-vmwgfx-bound-dma-command-body-size-against-suffix-pointer.patch
new file mode 100644 (file)
index 0000000..ed99e9a
--- /dev/null
@@ -0,0 +1,49 @@
+From f4f1db96bfd68b81053693ba53405b6f510ac16c Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:28 -0400
+Subject: drm/vmwgfx: bound DMA command body size against suffix pointer
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit f4f1db96bfd68b81053693ba53405b6f510ac16c upstream.
+
+vmw_cmd_dma() locates the DMA suffix at
+
+       (unsigned long) &cmd->body + header->size - sizeof(*suffix)
+
+without checking that header->size is large enough to contain both
+cmd->body and the suffix.  An undersized header makes the suffix
+pointer underflow back into the previous command in the bounce
+buffer.  The verifier later writes suffix->maximumOffset, clobbering
+verified fields of an already-relocated earlier command -- a TOCTOU
+on the device-visible command stream that lets one command rewrite
+another's GMR id, surface id, or other authenticated fields.
+
+Reject the command if the body is too small for the suffix to fit.
+
+Fixes: 4e4ddd477743 ("drm/vmwgfx: Fix queries if no dma buffer thrashing is occuring.")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-8-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+@@ -1510,6 +1510,12 @@ static int vmw_cmd_dma(struct vmw_privat
+       bool dirty;
+       cmd = container_of(header, typeof(*cmd), header);
++
++      if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) {
++              VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n");
++              return -EINVAL;
++      }
++
+       suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body +
+                                              header->size - sizeof(*suffix));
diff --git a/queue-6.18/drm-vmwgfx-clamp-dirty-page-range-with-min-not-max.patch b/queue-6.18/drm-vmwgfx-clamp-dirty-page-range-with-min-not-max.patch
new file mode 100644 (file)
index 0000000..20cf22e
--- /dev/null
@@ -0,0 +1,54 @@
+From f47d542d5912f236273399d7139b522f6950e2e4 Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:24 -0400
+Subject: drm/vmwgfx: clamp dirty-page range with min, not max
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit f47d542d5912f236273399d7139b522f6950e2e4 upstream.
+
+vmw_bo_dirty_transfer_to_res() and vmw_bo_dirty_clear() compute the
+intersection of a resource's page range with the BO's tracked dirty
+range, but clamp res_end against dirty->end with max() instead of
+min().  When dirty->end exceeds the resource end, the loop walks past
+the resource's pages, calls vmw_resource_dirty_update() for ranges
+owned by other resources sharing the same backing MOB and clears
+their pending dirty bits via bitmap_clear().  The result is silent
+loss of writeback for unrelated resources whenever two resources
+share a MOB.
+
+Use min() in both functions so the loop is bounded to the
+intersection of the resource and dirty ranges.
+
+Fixes: b7468b15d271 ("drm/vmwgfx: Implement an infrastructure for write-coherent resources")
+Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-4-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c
+@@ -311,7 +311,7 @@ void vmw_bo_dirty_transfer_to_res(struct
+               return;
+       cur = max(res_start, dirty->start);
+-      res_end = max(res_end, dirty->end);
++      res_end = min(res_end, dirty->end);
+       while (cur < res_end) {
+               unsigned long num;
+@@ -347,7 +347,7 @@ void vmw_bo_dirty_clear(struct vmw_bo *v
+               return;
+       cur = max(res_start, dirty->start);
+-      res_end = max(res_end, dirty->end);
++      res_end = min(res_end, dirty->end);
+       while (cur < res_end) {
+               unsigned long num;
diff --git a/queue-6.18/drm-vmwgfx-drop-dma_buf-reference-on-foreign-fd-prime-import.patch b/queue-6.18/drm-vmwgfx-drop-dma_buf-reference-on-foreign-fd-prime-import.patch
new file mode 100644 (file)
index 0000000..2b28df3
--- /dev/null
@@ -0,0 +1,52 @@
+From f739416dc555fa205a785e5135d73fa39b26f35d Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:26 -0400
+Subject: drm/vmwgfx: drop dma_buf reference on foreign-fd prime import
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit f739416dc555fa205a785e5135d73fa39b26f35d upstream.
+
+ttm_prime_fd_to_handle() returns -ENOSYS when the imported fd's
+dma_buf->ops do not match the ttm_object_device's ops, but does so
+without releasing the reference acquired by dma_buf_get().  Any
+unprivileged renderD client passing a non-vmwgfx prime fd through the
+DRM_VMW_GB_SURFACE_REF{,_EXT} path leaks one dma_buf reference per
+call and indefinitely pins the foreign exporter's GEM resources.
+
+Funnel the error path through the existing dma_buf_put() so the
+reference is always dropped.
+
+Fixes: 65981f7681ab ("drm/ttm: Add a minimal prime implementation for ttm base objects")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-6-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/ttm_object.c |    7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/ttm_object.c
++++ b/drivers/gpu/drm/vmwgfx/ttm_object.c
+@@ -547,14 +547,17 @@ int ttm_prime_fd_to_handle(struct ttm_ob
+       if (IS_ERR(dma_buf))
+               return PTR_ERR(dma_buf);
+-      if (dma_buf->ops != &tdev->ops)
+-              return -ENOSYS;
++      if (dma_buf->ops != &tdev->ops) {
++              ret = -ENOSYS;
++              goto out;
++      }
+       prime = (struct ttm_prime_object *) dma_buf->priv;
+       base = &prime->base;
+       *handle = base->handle;
+       ret = ttm_ref_object_add(tfile, base, NULL, false);
++out:
+       dma_buf_put(dma_buf);
+       return ret;
diff --git a/queue-6.18/drm-vmwgfx-enforce-cursor-size-limits-for-mob-cursors.patch b/queue-6.18/drm-vmwgfx-enforce-cursor-size-limits-for-mob-cursors.patch
new file mode 100644 (file)
index 0000000..c521423
--- /dev/null
@@ -0,0 +1,129 @@
+From d5ed8749168ad13c0dbaa8300f68d854b6076966 Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:30 -0400
+Subject: drm/vmwgfx: enforce cursor size limits for MOB cursors
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit d5ed8749168ad13c0dbaa8300f68d854b6076966 upstream.
+
+vmw_cursor_plane_atomic_check() bounds cursor width and height only
+on the legacy update path; the SVGA_CAP2_CURSOR_MOB path -- the
+default on modern hosts -- accepts any size.  When the requested size
+exceeds SVGA_REG_CURSOR_MAX_DIMENSION or SVGA_REG_MOB_MAX_SIZE,
+vmw_cursor_mob_get() returns -EINVAL and leaves vps->cursor.mob NULL.
+Its return value is then discarded in vmw_cursor_plane_prepare_fb(),
+so the subsequent vmw_cursor_update_mob() calls
+vmw_bo_map_and_cache(NULL) and oopses inside
+vmw_bo_map_and_cache_size() on the tbo.base.size load.
+
+Reachable from any DRM master via DRM_IOCTL_MODE_CURSOR2 with a
+sufficiently large width or height (e.g. cursor_max_dim + 1).
+
+Reject oversized cursors in atomic_check for both MOB-backed cursor
+update types.  The MOB byte-size limit only applies to the
+SVGA_CAP2_CURSOR_MOB path (vmw_cursor_mob_size() returns 0 for
+GB_ONLY); compute the required MOB size in 64-bit to avoid overflow
+when very large dimensions are requested.
+
+In prepare_fb only call vmw_cursor_mob_get()/_map() for
+VMW_CURSOR_UPDATE_MOB -- the GB_ONLY path uses bo->map.virtual
+directly and would otherwise be silently downgraded to NONE on hosts
+without SVGA_CAP2_CURSOR_MOB (where vmw_cursor_mob_get() always
+returns -EINVAL).  Degrade the update to NONE if vmw_cursor_mob_get()
+or vmw_cursor_mob_map() fails so the update path does not run with a
+NULL backing MOB.
+
+Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-10-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c |   49 ++++++++++++++++++++++++---
+ 1 file changed, 44 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
+@@ -432,6 +432,7 @@ vmw_cursor_mob_map(struct vmw_plane_stat
+       u32 size = vmw_cursor_mob_size(vps->cursor.update_type,
+                                      vps->base.crtc_w, vps->base.crtc_h);
+       struct vmw_bo *vbo = vps->cursor.mob;
++      void *map;
+       if (!vbo)
+               return -EINVAL;
+@@ -446,11 +447,15 @@ vmw_cursor_mob_map(struct vmw_plane_stat
+       if (unlikely(ret != 0))
+               return -ENOMEM;
+-      vmw_bo_map_and_cache(vbo);
++      map = vmw_bo_map_and_cache(vbo);
++      if (!map) {
++              vmw_bo_unmap(vbo);
++              ret = -ENOMEM;
++      }
+       ttm_bo_unreserve(&vbo->tbo);
+-      return 0;
++      return ret;
+ }
+ /**
+@@ -663,9 +668,15 @@ int vmw_cursor_plane_prepare_fb(struct d
+                           !vmw_cursor_buffer_changed(vps, old_vps)) {
+                               vps->cursor.update_type =
+                                       VMW_CURSOR_UPDATE_NONE;
+-                      } else {
+-                              vmw_cursor_mob_get(vcp, vps);
+-                              vmw_cursor_mob_map(vps);
++                      } else if (vps->cursor.update_type ==
++                                 VMW_CURSOR_UPDATE_MOB &&
++                                 (vmw_cursor_mob_get(vcp, vps) ||
++                                  vmw_cursor_mob_map(vps))) {
++                              /*
++                               * Reset the cursor to avoid crashes later.
++                               */
++                              vps->cursor.update_type =
++                                      VMW_CURSOR_UPDATE_NONE;
+                       }
+               }
+       }
+@@ -732,6 +743,34 @@ int vmw_cursor_plane_atomic_check(struct
+                                "surface not suitable for cursor\n");
+                       return -EINVAL;
+               }
++      } else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY ||
++                 update_type == VMW_CURSOR_UPDATE_MOB) {
++              u32 cursor_max_dim =
++                      vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION);
++
++              if (new_state->crtc_w > cursor_max_dim ||
++                  new_state->crtc_h > cursor_max_dim) {
++                      drm_warn(&vmw->drm,
++                               "Cursor dimensions (%d, %d) exceed device max %u\n",
++                               new_state->crtc_w, new_state->crtc_h,
++                               cursor_max_dim);
++                      return -EINVAL;
++              }
++
++              if (update_type == VMW_CURSOR_UPDATE_MOB) {
++                      u32 mob_max_size =
++                              vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE);
++                      u64 mob_size = (u64)new_state->crtc_w *
++                                     new_state->crtc_h * sizeof(u32) +
++                                     sizeof(SVGAGBCursorHeader);
++
++                      if (mob_size > mob_max_size) {
++                              drm_warn(&vmw->drm,
++                                       "Cursor MOB size %llu exceeds device max %u\n",
++                                       mob_size, mob_max_size);
++                              return -EINVAL;
++                      }
++              }
+       }
+       return 0;
diff --git a/queue-6.18/drm-vmwgfx-fix-guest_memory_dirty-bitfield-clobbered-as-size.patch b/queue-6.18/drm-vmwgfx-fix-guest_memory_dirty-bitfield-clobbered-as-size.patch
new file mode 100644 (file)
index 0000000..c7eb2b7
--- /dev/null
@@ -0,0 +1,59 @@
+From 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:22 -0400
+Subject: drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa upstream.
+
+Two sites in vmwgfx_resource.c assign boolean literals to
+res->guest_memory_size, which is an unsigned long allocation-size
+field; the intended target is the adjacent res->guest_memory_dirty
+bitfield.  After the assignments the field holds 0 or 1 instead of
+the resource's MOB allocation size:
+
+  - vmw_resource_release()       writes 0 (false), and
+  - vmw_resource_unbind_list()   writes 1 (true).
+
+Subsequent revalidation paths read guest_memory_size when computing
+the dirty page range (vmw_bo_dirty_transfer_to_res()) and the buffer
+allocation size (vmw_resource_buf_alloc()), producing zero-length
+walks or wrap-around ranges that read or write past the MOB bitmap.
+The dirty-tracking intent of the original code (mark the resource as
+dirtied since the last sync) is also lost, since guest_memory_dirty
+is never updated.
+
+Rename both assignments to guest_memory_dirty.
+
+Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-2-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_resource.c |    4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+@@ -136,7 +136,7 @@ static void vmw_resource_release(struct
+                       val_buf.num_shared = 0;
+                       res->func->unbind(res, false, &val_buf);
+               }
+-              res->guest_memory_size = false;
++              res->guest_memory_dirty = false;
+               vmw_resource_mob_detach(res);
+               if (res->dirty)
+                       res->func->dirty_free(res);
+@@ -773,7 +773,7 @@ void vmw_resource_unbind_list(struct vmw
+               if (!WARN_ON_ONCE(!res->func->unbind))
+                       (void) res->func->unbind(res, res->res_dirty, &val_buf);
+-              res->guest_memory_size = true;
++              res->guest_memory_dirty = true;
+               res->res_dirty = false;
+               vmw_resource_mob_detach(res);
+       }
diff --git a/queue-6.18/drm-vmwgfx-reject-dx_bind_query-without-a-dx-context.patch b/queue-6.18/drm-vmwgfx-reject-dx_bind_query-without-a-dx-context.patch
new file mode 100644 (file)
index 0000000..a588eac
--- /dev/null
@@ -0,0 +1,57 @@
+From 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:23 -0400
+Subject: drm/vmwgfx: reject DX_BIND_QUERY without a DX context
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db upstream.
+
+vmw_cmd_dx_bind_query() unconditionally dereferences
+sw_context->dx_ctx_node->ctx.  Userspace can trigger a NULL pointer
+dereference from any render-node fd by submitting an execbuf with
+dx_context_handle == SVGA3D_INVALID_ID and a SVGA_3D_CMD_DX_BIND_QUERY
+opcode in the command stream: dx_ctx_node is left NULL and the kernel
+oopses on the assignment.  The same NULL is then re-read in
+vmw_resources_reserve() via vmw_context_get_dx_query_mob().
+
+All sibling DX handlers fail-close on a missing dx_ctx_node using
+VMW_GET_CTX_NODE().  Use the same pattern here, returning -EINVAL up
+front before any relocation state is published.
+
+Fixes: 9c079b8ce8bf ("drm/vmwgfx: Adapt execbuf to the new validation api")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-3-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c |    6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+@@ -1272,9 +1272,13 @@ static int vmw_cmd_dx_bind_query(struct
+                                SVGA3dCmdHeader *header)
+ {
+       VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindQuery);
++      struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context);
+       struct vmw_bo *vmw_bo;
+       int ret;
++      if (!ctx_node)
++              return -EINVAL;
++
+       cmd = container_of(header, typeof(*cmd), header);
+       /*
+@@ -1288,7 +1292,7 @@ static int vmw_cmd_dx_bind_query(struct
+               return ret;
+       sw_context->dx_query_mob = vmw_bo;
+-      sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx;
++      sw_context->dx_query_ctx = ctx_node->ctx;
+       return 0;
+ }
diff --git a/queue-6.18/drm-vmwgfx-take-fman-lock-around-fence-list-mutation-in-fifo_down.patch b/queue-6.18/drm-vmwgfx-take-fman-lock-around-fence-list-mutation-in-fifo_down.patch
new file mode 100644 (file)
index 0000000..19aeadb
--- /dev/null
@@ -0,0 +1,64 @@
+From 250af2e8c3e90dc978e062a936b633870a22e660 Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:25 -0400
+Subject: drm/vmwgfx: take fman->lock around fence list mutation in fifo_down
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit 250af2e8c3e90dc978e062a936b633870a22e660 upstream.
+
+vmw_fence_fifo_down() drops fman->lock to wait on a fence and, on
+timeout, mutates fman->fence_list via list_del_init() and signals
+the fence without re-acquiring the lock.  __vmw_fences_update() walks
+and removes entries from the same list under fman->lock from any
+other waiter, the fence-IRQ thread, or vmw_fences_update(), so the
+unlocked list_del_init() can corrupt the list head.
+
+Re-take fman->lock before manipulating fence->head and use
+dma_fence_signal_locked().  Wrap the locked signalling in
+dma_fence_begin_signalling() / dma_fence_end_signalling() so the
+lockdep annotation that dma_fence_signal() previously provided is
+preserved (the same pattern as __vmw_fences_update()).
+
+dma_fence_put() is moved outside the lock to avoid a recursive
+acquire from vmw_fence_obj_destroy(), which also takes fman->lock.
+
+Fixes: ae2a104058e2 ("vmwgfx: Implement fence objects")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-5-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_fence.c |   13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+@@ -366,13 +366,24 @@ void vmw_fence_fifo_down(struct vmw_fenc
+               ret = vmw_fence_obj_wait(fence, false, false,
+                                        VMW_FENCE_WAIT_TIMEOUT);
++              spin_lock(&fman->lock);
+               if (unlikely(ret != 0)) {
++                      bool cookie = dma_fence_begin_signalling();
++
+                       list_del_init(&fence->head);
+-                      dma_fence_signal(&fence->base);
++                      if (fence->waiter_added) {
++                              vmw_seqno_waiter_remove(fman->dev_priv);
++                              fence->waiter_added = false;
++                      }
++                      dma_fence_signal_locked(&fence->base);
++                      dma_fence_end_signalling(cookie);
+               }
+               BUG_ON(!list_empty(&fence->head));
++              spin_unlock(&fman->lock);
++
+               dma_fence_put(&fence->base);
++
+               spin_lock(&fman->lock);
+       }
+       spin_unlock(&fman->lock);
diff --git a/queue-6.18/drm-vmwgfx-use-check_add_overflow-for-shader-size-offset-bound.patch b/queue-6.18/drm-vmwgfx-use-check_add_overflow-for-shader-size-offset-bound.patch
new file mode 100644 (file)
index 0000000..8f60752
--- /dev/null
@@ -0,0 +1,90 @@
+From 54d56d5b42d2e4c72ba6e365e9774da90698aa22 Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:32 -0400
+Subject: drm/vmwgfx: use check_add_overflow for shader size+offset bound
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit 54d56d5b42d2e4c72ba6e365e9774da90698aa22 upstream.
+
+vmw_shader_define() validates the user-supplied shader window against
+its backing buffer with
+
+       (u64)buffer->tbo.base.size < (u64)size + (u64)offset
+
+drm_vmw_shader_create_arg::offset is __u64 in the uapi; when it is
+near U64_MAX the unsigned addition wraps and the resulting tiny value
+passes the check.  The unbounded offset is then stored in
+res->guest_memory_offset and forwarded to host SVGA shader-create
+commands.
+
+Use check_add_overflow() to detect the wrap and compare the resulting
+endpoint against the buffer size.
+
+Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-12-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_shader.c |   13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+@@ -25,6 +25,8 @@
+  *
+  **************************************************************************/
++#include <linux/overflow.h>
++
+ #include <drm/ttm/ttm_placement.h>
+ #include "vmwgfx_binding.h"
+@@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_
+ static int vmw_user_shader_alloc(struct vmw_private *dev_priv,
+                                struct vmw_bo *buffer,
+                                size_t shader_size,
+-                               size_t offset,
++                               u64 offset,
+                                SVGA3dShaderType shader_type,
+                                uint8_t num_input_sig,
+                                uint8_t num_output_sig,
+@@ -739,7 +741,7 @@ out:
+ static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv,
+                                            struct vmw_bo *buffer,
+                                            size_t shader_size,
+-                                           size_t offset,
++                                           u64 offset,
+                                            SVGA3dShaderType shader_type)
+ {
+       struct vmw_shader *shader;
+@@ -768,7 +770,7 @@ out_err:
+ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv,
+                            enum drm_vmw_shader_type shader_type_drm,
+-                           u32 buffer_handle, size_t size, size_t offset,
++                           u32 buffer_handle, size_t size, u64 offset,
+                            uint8_t num_input_sig, uint8_t num_output_sig,
+                            uint32_t *shader_handle)
+ {
+@@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_
+       int ret;
+       if (buffer_handle != SVGA3D_INVALID_ID) {
++              u64 end;
++
+               ret = vmw_user_bo_lookup(file_priv, buffer_handle, &buffer);
+               if (unlikely(ret != 0)) {
+                       VMW_DEBUG_USER("Couldn't find buffer for shader creation.\n");
+                       return ret;
+               }
+-              if ((u64)buffer->tbo.base.size < (u64)size + (u64)offset) {
++              if (check_add_overflow((u64)size, (u64)offset, &end) ||
++                  end > buffer->tbo.base.size) {
+                       VMW_DEBUG_USER("Illegal buffer- or shader size.\n");
+                       ret = -EINVAL;
+                       goto out_bad_arg;
diff --git a/queue-6.18/drm-vmwgfx-validate-draw_primitives-header-size-before-division.patch b/queue-6.18/drm-vmwgfx-validate-draw_primitives-header-size-before-division.patch
new file mode 100644 (file)
index 0000000..b678f73
--- /dev/null
@@ -0,0 +1,54 @@
+From 85891d174707d8bddcec7a888fb4e1d17def34f3 Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:27 -0400
+Subject: drm/vmwgfx: validate DRAW_PRIMITIVES header size before division
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit 85891d174707d8bddcec7a888fb4e1d17def34f3 upstream.
+
+vmw_cmd_draw() computes
+
+       maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
+
+where header->size is u32 and is taken straight from the user-supplied
+command stream.  When header->size is less than sizeof(cmd->body) the
+unsigned subtraction wraps to nearly 4 GiB, producing a huge maxnum.
+Any user-controlled cmd->body.numVertexDecls then passes the bound and
+the loop dereferences decl[i] far past the end of the kernel command
+bounce buffer, producing an out-of-bounds read of kernel memory.
+
+Reject undersized headers up front.
+
+Fixes: 7a73ba7469cb ("drm/vmwgfx: Use TTM handles instead of SIDs as user-space surface handles.")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-7-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c |    8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+@@ -1571,11 +1571,17 @@ static int vmw_cmd_draw(struct vmw_priva
+       uint32_t maxnum;
+       int ret;
++      cmd = container_of(header, typeof(*cmd), header);
++
++      if (unlikely(header->size < sizeof(cmd->body))) {
++              VMW_DEBUG_USER("Illegal DRAW_PRIMITIVES header size.\n");
++              return -EINVAL;
++      }
++
+       ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
+       if (unlikely(ret != 0))
+               return ret;
+-      cmd = container_of(header, typeof(*cmd), header);
+       maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
+       if (unlikely(cmd->body.numVertexDecls > maxnum)) {
diff --git a/queue-6.18/drm-vmwgfx-validate-external-bo-copy-bounds-for-both-stride-paths.patch b/queue-6.18/drm-vmwgfx-validate-external-bo-copy-bounds-for-both-stride-paths.patch
new file mode 100644 (file)
index 0000000..67985ec
--- /dev/null
@@ -0,0 +1,131 @@
+From 706c93c5813caabbb0d0a576c017d15aeec2c113 Mon Sep 17 00:00:00 2001
+From: Zack Rusin <zack.rusin@broadcom.com>
+Date: Tue, 5 May 2026 18:22:33 -0400
+Subject: drm/vmwgfx: validate external BO copy bounds for both stride paths
+
+From: Zack Rusin <zack.rusin@broadcom.com>
+
+commit 706c93c5813caabbb0d0a576c017d15aeec2c113 upstream.
+
+vmw_external_bo_copy() trusts caller-supplied offsets, strides, and
+heights and operates on imported dma-buf vmaps:
+
+  - The equal-stride memcpy() bound was clamped after subtracting the
+    offsets from dst_size and src_size; an offset larger than the BO
+    size wraps the unsigned subtraction to a huge value and the
+    resulting memcpy() runs off the end of the vmap.  dst_stride *
+    height is also a u32 multiplication that can overflow.
+  - The non-equal-stride row-by-row path had no bound at all.  The
+    loop touches bytes through offset + (height - 1) * stride +
+    width_in_bytes, with only a WARN_ON(dst_stride < width_in_bytes),
+    and could likewise step past the end of either mapping.
+
+The offsets and strides are derived from STDU/SOU plane state, so a
+configured CRTC submitting a crafted atomic commit on an imported
+framebuffer can reach this path.
+
+Validate the exact row-copy endpoint against each BO's size up front
+using check_mul_overflow() and check_add_overflow().  Use the bulk
+memcpy() path only when width_in_bytes covers the whole stride;
+otherwise copy one row at a time so partial-row updates near the bottom
+of a framebuffer remain valid.  Also reject zero strides and stride <
+width_in_bytes, both of which the row-by-row path cannot represent
+safely.
+
+Fixes: 50f119925091 ("drm/vmwgfx: Fix prime with external buffers")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4.7
+Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
+Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
+Link: https://patch.msgid.link/20260505222728.519626-13-zack.rusin@broadcom.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/vmwgfx/vmwgfx_blit.c |   39 +++++++++++++++++++++++++++--------
+ 1 file changed, 31 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
+@@ -30,6 +30,7 @@
+ #include "vmwgfx_bo.h"
+ #include <linux/highmem.h>
++#include <linux/overflow.h>
+ /*
+  * Template that implements find_first_diff() for a generic
+@@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct v
+               container_of(dst->tbo.bdev, struct vmw_private, bdev);
+       size_t dst_size = dst->tbo.resource->size;
+       size_t src_size = src->tbo.resource->size;
++      size_t dst_end, src_end;
+       struct iosys_map dst_map = {0};
+       struct iosys_map src_map = {0};
++      bool dst_mapped = false;
++      bool src_mapped = false;
+       int ret, i;
+       int x_in_bytes;
+       u8 *vsrc;
+       u8 *vdst;
++      if (!height || !width_in_bytes)
++              return 0;
++
++      if (!dst_stride || !src_stride)
++              return -EINVAL;
++      if (dst_stride < width_in_bytes || src_stride < width_in_bytes)
++              return -EINVAL;
++      if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, &dst_end) ||
++          check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) ||
++          check_add_overflow((size_t)dst_offset, dst_end, &dst_end) ||
++          dst_end > dst_size ||
++          check_mul_overflow((size_t)src_stride, (size_t)height - 1, &src_end) ||
++          check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) ||
++          check_add_overflow((size_t)src_offset, src_end, &src_end) ||
++          src_end > src_size) {
++              drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n");
++              return -EINVAL;
++      }
++
+       vsrc = map_external(src, &src_map);
+       if (!vsrc) {
+               drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n");
+               ret = -ENOMEM;
+               goto out;
+       }
++      src_mapped = true;
+       vdst = map_external(dst, &dst_map);
+       if (!vdst) {
+@@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct v
+               ret = -ENOMEM;
+               goto out;
+       }
++      dst_mapped = true;
+       vsrc += src_offset;
+       vdst += dst_offset;
+-      if (src_stride == dst_stride) {
+-              dst_size -= dst_offset;
+-              src_size -= src_offset;
+-              memcpy(vdst, vsrc,
+-                     min(dst_stride * height, min(dst_size, src_size)));
++      if (src_stride == dst_stride && width_in_bytes == dst_stride) {
++              memcpy(vdst, vsrc, dst_stride * (size_t)height);
+       } else {
+-              WARN_ON(dst_stride < width_in_bytes);
+               for (i = 0; i < height; ++i) {
+                       memcpy(vdst, vsrc, width_in_bytes);
+                       vsrc += src_stride;
+@@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct v
+       ret = 0;
+ out:
+-      unmap_external(src, &src_map);
+-      unmap_external(dst, &dst_map);
++      if (src_mapped)
++              unmap_external(src, &src_map);
++      if (dst_mapped)
++              unmap_external(dst, &dst_map);
+       return ret;
+ }
index 83e3e8d4595a53cf50f912b8a80f52dac2574171..f66d16ed4244929d931d737ef205e60d934b4995 100644 (file)
@@ -328,3 +328,14 @@ drm-amdkfd-fix-qid-bit-leak-in-pqm_create_queue.patch
 drm-amdkfd-fix-uint32_t-overflow-in-eop-ring-buffer-size-alignment.patch
 drm-amdkfd-handle-invalid-event-type-in-criu-event-restore.patch
 drm-amdkfd-hold-event_mutex-while-checkpointing-criu-events.patch
+drm-vmwgfx-fix-guest_memory_dirty-bitfield-clobbered-as-size.patch
+drm-vmwgfx-reject-dx_bind_query-without-a-dx-context.patch
+drm-vmwgfx-clamp-dirty-page-range-with-min-not-max.patch
+drm-vmwgfx-take-fman-lock-around-fence-list-mutation-in-fifo_down.patch
+drm-vmwgfx-drop-dma_buf-reference-on-foreign-fd-prime-import.patch
+drm-vmwgfx-validate-draw_primitives-header-size-before-division.patch
+drm-vmwgfx-bound-dma-command-body-size-against-suffix-pointer.patch
+drm-vmwgfx-avoid-destroy_workqueue-null-on-vkms-init-failure.patch
+drm-vmwgfx-enforce-cursor-size-limits-for-mob-cursors.patch
+drm-vmwgfx-use-check_add_overflow-for-shader-size-offset-bound.patch
+drm-vmwgfx-validate-external-bo-copy-bounds-for-both-stride-paths.patch