From: Greg Kroah-Hartman Date: Wed, 5 Aug 2026 12:19:59 +0000 (+0200) Subject: 6.18-stable patches X-Git-Tag: v5.10.263~20 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7b3c902d148a8f21399087291089594a753e5fb;p=thirdparty%2Fkernel%2Fstable-queue.git 6.18-stable patches 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 --- 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 index 0000000000..8559b10d52 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-avoid-destroy_workqueue-null-on-vkms-init-failure.patch @@ -0,0 +1,74 @@ +From 05eaa887e7b4f40fba425f8a1d7a5a8a043092a6 Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:29 -0400 +Subject: drm/vmwgfx: avoid destroy_workqueue(NULL) on vkms init failure + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-9-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..ed99e9a7fd --- /dev/null +++ b/queue-6.18/drm-vmwgfx-bound-dma-command-body-size-against-suffix-pointer.patch @@ -0,0 +1,49 @@ +From f4f1db96bfd68b81053693ba53405b6f510ac16c Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:28 -0400 +Subject: drm/vmwgfx: bound DMA command body size against suffix pointer + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-8-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..20cf22e8c4 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-clamp-dirty-page-range-with-min-not-max.patch @@ -0,0 +1,54 @@ +From f47d542d5912f236273399d7139b522f6950e2e4 Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:24 -0400 +Subject: drm/vmwgfx: clamp dirty-page range with min, not max + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-4-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..2b28df37d5 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-drop-dma_buf-reference-on-foreign-fd-prime-import.patch @@ -0,0 +1,52 @@ +From f739416dc555fa205a785e5135d73fa39b26f35d Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:26 -0400 +Subject: drm/vmwgfx: drop dma_buf reference on foreign-fd prime import + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-6-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..c521423ec7 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-enforce-cursor-size-limits-for-mob-cursors.patch @@ -0,0 +1,129 @@ +From d5ed8749168ad13c0dbaa8300f68d854b6076966 Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:30 -0400 +Subject: drm/vmwgfx: enforce cursor size limits for MOB cursors + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-10-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..c7eb2b7f65 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-fix-guest_memory_dirty-bitfield-clobbered-as-size.patch @@ -0,0 +1,59 @@ +From 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:22 -0400 +Subject: drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-2-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..a588eacc53 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-reject-dx_bind_query-without-a-dx-context.patch @@ -0,0 +1,57 @@ +From 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:23 -0400 +Subject: drm/vmwgfx: reject DX_BIND_QUERY without a DX context + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-3-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..19aeadbd67 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-take-fman-lock-around-fence-list-mutation-in-fifo_down.patch @@ -0,0 +1,64 @@ +From 250af2e8c3e90dc978e062a936b633870a22e660 Mon Sep 17 00:00:00 2001 +From: Zack Rusin +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 + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-5-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..8f60752cb8 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-use-check_add_overflow-for-shader-size-offset-bound.patch @@ -0,0 +1,90 @@ +From 54d56d5b42d2e4c72ba6e365e9774da90698aa22 Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:32 -0400 +Subject: drm/vmwgfx: use check_add_overflow for shader size+offset bound + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-12-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++ + #include + + #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 index 0000000000..b678f73bab --- /dev/null +++ b/queue-6.18/drm-vmwgfx-validate-draw_primitives-header-size-before-division.patch @@ -0,0 +1,54 @@ +From 85891d174707d8bddcec7a888fb4e1d17def34f3 Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:27 -0400 +Subject: drm/vmwgfx: validate DRAW_PRIMITIVES header size before division + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-7-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..67985ec0d6 --- /dev/null +++ b/queue-6.18/drm-vmwgfx-validate-external-bo-copy-bounds-for-both-stride-paths.patch @@ -0,0 +1,131 @@ +From 706c93c5813caabbb0d0a576c017d15aeec2c113 Mon Sep 17 00:00:00 2001 +From: Zack Rusin +Date: Tue, 5 May 2026 18:22:33 -0400 +Subject: drm/vmwgfx: validate external BO copy bounds for both stride paths + +From: Zack Rusin + +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 +Reviewed-by: Ian Forbes +Link: https://patch.msgid.link/20260505222728.519626-13-zack.rusin@broadcom.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 ++#include + + /* + * 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; + } diff --git a/queue-6.18/series b/queue-6.18/series index 83e3e8d459..f66d16ed42 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -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