From: Michal TOMA <[email protected]> v1 was a single patch, "drm/vmwgfx: Release PRIME import in the BO destroy path":
https://lore.kernel.org/r/[email protected] Please do not apply that one on its own. An automated review flagged an interaction with the existing TTM_TT_FLAG_EXTERNAL handling in vmw_ttm_map_dma(), and it turned out to be real: with v1 applied and nothing else, an unprivileged render node client can oops the kernel. That is what patch 1 of this series fixes, and it has to come first. 1/3 drm/vmwgfx: Don't map or free the exporter's sg_table for imported BOs Fixes: b32233acceff, Cc: stable # v6.9+ 2/3 drm/vmwgfx: Release PRIME import in the BO destroy path (v1, unchanged) Fixes: b32233acceff, Cc: stable # v6.6+ 3/3 drm/vmwgfx: Don't leak a GEM handle when referencing a surface by fd Fixes: d6667f0ddf46, Cc: stable # v6.11+ Changes since v1: - new 1/3. Without it, 2/3 turns a silent leak into a NULL pointer dereference. - new 3/3. It was written before v1 was posted but held back, since the two fixes are independent. - 2/3 is byte for byte what was posted as v1. Why the order matters --------------------- For a TTM tt with TTM_TT_FLAG_EXTERNAL, vmw_ttm_map_dma() points vsgt->sgt at the exporter's sg_table and then calls vmw_ttm_map_for_dma(), which maps &vmw_tt->sgt: the inline table, which for an imported buffer is never populated. dma_map_sgtable() is called with orig_nents == 0, warns, and returns -EIO, and out_map_fail then calls sg_free_table() on the exporter's table, leaving sgl == NULL and orig_nents unchanged. Today that only corrupts and leaks, because vmwgfx never releases a PRIME import, so nothing ever unmaps the attachment. 2/3 fixes exactly that leak, and in doing so makes the exporter unmap the table that vmwgfx already freed: dma_unmap_sgtable(dev, sg with sgl == NULL, orig_nents == N) dma_direct_unmap_sg() -> for_each_sg(NULL, sg, N, i) which dereferences NULL, in the TTM delayed-delete worker. 1/3 is worth having on its own: binding an imported dma-buf has never worked, it always failed with -EIO and damaged the exporter's table on the way out. With 1/3 it works. Reachability, and how this was reproduced ----------------------------------------- Three ioctls, all DRM_RENDER_ALLOW, as an ordinary user: import a dma-buf with DRM_IOCTL_PRIME_FD_TO_HANDLE (a udmabuf here), create a guest-backed surface on it with DRM_VMW_GB_SURFACE_CREATE_EXT (base.buffer_handle = the imported handle), then submit SVGA_3D_CMD_UPDATE_GB_SURFACE for that surface with DRM_VMW_EXECBUF. Validation moves the buffer to VMW_BO_DOMAIN_MOB, which binds the tt. Without 1/3, with 2/3 applied: WARNING: kernel/dma/mapping.c:266 at __dma_map_sg_attrs+0xdd/0x1d0 dma_map_sgtable+0x1d/0x30 vmw_ttm_map_dma+0xf6/0x140 [vmwgfx] vmw_move+0x1cd/0x2c0 [vmwgfx] ttm_bo_handle_move_mem+0xc0/0x180 [ttm] ttm_bo_validate+0xd2/0x1d0 [ttm] vmw_validation_bo_validate+0xb5/0x180 [vmwgfx] vmw_execbuf_process+0x852/0x1330 [vmwgfx] vmw_execbuf_ioctl+0x10d/0x1d0 [vmwgfx] vmwgfx 0000:00:02.0: [drm] VSG table map failed! and then, when the buffer is released: BUG: kernel NULL pointer dereference, address: 000000000000001c Workqueue: ttm ttm_bo_delayed_delete [ttm] RIP: 0010:dma_direct_unmap_sg+0x62/0x200 unmap_udmabuf+0x24/0x40 dma_buf_unmap_attachment_unlocked+0x46/0x70 drm_prime_gem_destroy+0x28/0x50 vmw_bo_free+0x15b/0x1f0 [vmwgfx] The closing process exits normally, since this runs in the delete worker; the worker dies holding TTM and dma-resv locks and the machine wedges shortly afterwards. With the full series, the same program logs nothing, the execbuf is accepted, and the buffer is released with no entry left in /sys/kernel/debug/dma_buf/bufinfo. Test results ------------ Kernel 7.2.3 on the guest below, comparing modules built from its own vmwgfx sources, which are identical to drm-misc-fixes for the files involved. import + surface + execbuf + release, as above: 2/3 only: WARN, "VSG table map failed!", execbuf -EIO, then an oops in ttm_bo_delayed_delete and a hang 1/3 + 2/3 + 3/3: no WARN, execbuf accepted, buffer released, no oops, kernel taint unchanged PRIME_FD_TO_HANDLE reproducer, 8 x 4 MiB udmabufs (2/3): stock: all 8 remain, count 1, still attached series: none remain, with 3D acceleration on and off DRM_VMW_GB_SURFACE_REF_EXT(PRIME) from a second render file (3/3), each checked while that file was still open: udmabuf fd, 8 calls: without 3/3: -EINVAL, 8 imports held (count 3), 8 WARNs with 3/3: -EINVAL, nothing imported, no WARN dumb buffer fd, 4 calls: without 3/3: -EINVAL, 4 stray GEM handles, 4 WARNs with 3/3: -EINVAL, no stray handle, no WARN buffer exported before a surface was created on it, 4 calls (reaches the changed function and succeeds): without 3/3: surface returned, 4 stray GEM handles with 3/3: surface returned, no stray handle buffer created together with its surface (a TTM prime export that never reaches the changed function), 4 calls: unchanged either way KWin 6.7.4, 5-minute replay of a terminal workload, 3D on: 2/3 only: udmabufs 4 -> 20 (140 MiB), never released, WARNs 2/3 + 3/3: udmabufs 4 -> 6 -> 4, no pinned pages, no WARN (not re-run with 1/3 in place; 1/3 does not touch that path) Test environment ---------------- - VirtualBox 7.2 (Guest Additions 7.2.16), VMSVGA adapter, 4 vCPUs, 3.8 GiB RAM. Tested with 3D acceleration on (vmwgfx shader model SM_5) and off (Legacy). DMA map mode is vmw_dma_map_populate. - openSUSE Tumbleweed kernel 7.2.3-1-default. The vmwgfx files involved are byte-identical to drm-misc-fixes 4600b4d1a9ee. The test modules were built out of tree from those sources, which adds the E taint. - The kernel was already tainted W+O before any test: two boot-time warnings unrelated to vmwgfx (arch/x86/mm/pat/set_memory.c:727 and kernel/rcu/tree_plugin.h:823), plus VirtualBox's out-of-tree vboxguest/vboxsf. - KDE Plasma / KWin 6.7.4, Mesa 26.2.1. - W=1 build of drivers/gpu/drm/vmwgfx/ on drm-misc-fixes: no warnings before or after. checkpatch.pl --strict is clean on all three. Not tested ---------- - VMware Workstation or ESXi hosts; only VirtualBox VMSVGA. - A full kernel built from drm-misc-fixes; only the 7.2.3 kernel with modules built from identical vmwgfx sources. - Kernels with KASAN, lockdep or kmemleak enabled. IGT was not run. - Stable backports were not built. 3/3 uses drm_gem_is_prime_exported_dma_buf(), added in v6.17 (660cd44659a0); older trees carrying d6667f0ddf46 would need that helper as well. Not fixed here -------------- The review of v1 also pointed out that vmw_prime_import_sg_table() returns NULL rather than an ERR_PTR when vmw_bo_create() fails, and that drm_gem_prime_import_dev() only checks IS_ERR() before dereferencing the result. That is real, but reaching it needs a buffer object allocation failure; I have not managed to trigger it, so I am not posting an untested patch for it here. Classification -------------- Per Documentation/process/threat-model.rst I am treating these as regular bugs. The leaks are bounded by the importing process's memory cgroup, and the crash needs 2/3 applied without 1/3, which exists only in this posting and in the v1 mail. Workaround for the leaks ------------------------ KWIN_DISABLE_UDMABUF_IMPORT=1 in the session environment makes KWin 6.7 use its copy path, which avoids the imports entirely. Tool use -------- I found the problem because my VM repeatedly ran out of memory and had to be rebooted. An LLM coding assistant (Claude, Anthropic) did the diagnostics that traced it back to this code, wrote these patches and their changelogs, and built the reproducers and the test tooling used to confirm the results above, including the crash that prompted 1/3. Michal TOMA (3): drm/vmwgfx: Don't map or free the exporter's sg_table for imported BOs drm/vmwgfx: Release PRIME import in the BO destroy path drm/vmwgfx: Don't leak a GEM handle when referencing a surface by fd drivers/gpu/drm/vmwgfx/vmwgfx_bo.c | 3 ++ drivers/gpu/drm/vmwgfx/vmwgfx_surface.c | 48 +++++++++++++--------- drivers/gpu/drm/vmwgfx/vmwgfx_ttm_buffer.c | 20 +++++++-- 3 files changed, 47 insertions(+), 24 deletions(-) base-commit: 4600b4d1a9ee730d03ddac5ce409cd2730ce8c0c -- 2.55.0
