Hi Maaz, I reproduced the bug on VMware Workstation with a kernel based on mainline (commit 6edd14dd67d7, v7.3-rc4), with the vgem test modification described below. Below is the setup, the KASAN splat, and notes on reproducing.
Regarding your October 3 question about drm-misc-fixes: as of 2026-10-06, I searched for "vmw_gem_object_get_sg_table drm-misc-fixes" and found no indexed commit fixing this function's embedded sg_table ownership issue. The published drm-misc-fixes-2026-10-01 pull request lists vmwgfx input validation and blend-mode changes, not this fix [1]. I could not access the branch's live file history, so I cannot confirm that no commit touching vmw_gem_object_get_sg_table exists at its current tip. The vgem import-path change described below can prevent the test from reaching the affected callback without fixing that callback. [1] https://www.mail-archive.com/dri-devel%40lists.freedesktop.org/msg641567.html Setup: - VMware Workstation 26.0.0 (build 25388281) on Ubuntu 24.04 host - Guest: Debian 12 (bookworm), CONFIG_KASAN=y - Kernel: commit 6edd14dd67d76d39a518ad3bf1a98363690a5a62 (v7.3-rc4) - vmwgfx loaded as module (out-of-tree rebuild from same tree) - Second DRM device: vgem (with gem_prime_import_sg_table enabled, see note below) - PoC runs as UID 65534 (nobody), no capabilities KASAN splat (the v7.3-rc4 commit above, VMware Workstation): The runtime release string in the unedited trace below is 7.3.0-rc4+. BUG: KASAN: invalid-free in dma_buf_unmap_attachment+0xaa/0x1d0 Free of addr ffff888104489960 by task poc_sgtable/354 CPU: 0 UID: 65534 PID: 354 Comm: poc_sgtable Tainted: G OE 7.3.0-rc4+ #16 Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 02/17/2026 Call Trace: <TASK> dump_stack_lvl+0x60/0x80 print_report+0xd0/0x630 kasan_report_invalid_free+0x9e/0xc0 check_slab_allocation+0xf5/0x100 kfree+0x166/0x420 dma_buf_unmap_attachment+0xaa/0x1d0 dma_buf_unmap_attachment_unlocked+0x80/0x100 drm_prime_gem_destroy+0x42/0x90 [drm] drm_gem_shmem_release+0x71/0x800 [drm_shmem_helper] drm_gem_shmem_object_free+0x9/0x20 [drm_shmem_helper] drm_gem_object_release_handle+0xaf/0x220 [drm] drm_gem_handle_delete+0x59/0xa0 [drm] drm_ioctl_kernel+0x163/0x2d0 [drm] drm_ioctl+0x4ce/0xb10 [drm] __x64_sys_ioctl+0x135/0x1c0 do_syscall_64+0xc1/0x560 entry_SYSCALL_64_after_hwframe+0x76/0x7e Steps to reproduce: 1. DRM_VMW_ALLOC_DMABUF(262144) on vmwgfx renderD128 2. DRM_IOCTL_PRIME_HANDLE_TO_FD 3. DRM_VMW_GB_SURFACE_CREATE_EXT 64x64 (BO size 262144 bytes) 4. EXECBUF BIND_GB_SURFACE(sid, mobid=handle) -> vmw_ttm_bind -> vmw_ttm_map_dma -> caches vsgt.sgt = &vmw_tt->sgt (embedded member) 5. DRM_IOCTL_PRIME_FD_TO_HANDLE on a second DRM device (vgem) -> dma_buf_map_attachment -> drm_gem_map_dma_buf -> vmw_gem_object_get_sg_table returns &vmw_tt->sgt 6. Close the importing GEM handle (DRM_IOCTL_GEM_CLOSE or fd close) -> drm_prime_gem_destroy -> dma_buf_unmap_attachment -> drm_gem_unmap_dma_buf: sg_free_table + kfree(&vmw_tt->sgt) (drm_gem_unmap_dma_buf elided in trace by tail-call optimization) => KASAN: invalid-free (interior pointer of vmw_ttm_tt object) Note on the importing device: The bug is in vmwgfx's vmw_gem_object_get_sg_table() which returns an interior pointer (&vmw_tt->sgt) that the DRM core later kfree()s. Any importing driver that calls dma_buf_map_attachment() triggers it, provided the BO has been DMA-mapped (vsgt.sgt cached by a prior surface bind), as demonstrated with vgem configured with gem_prime_import_sg_table. This was tested with vgem; the statement about other importing drivers is an inference from the shared PRIME map/unmap path. On mainline, vgem recently switched to drm_gem_shmem_prime_import_no_map (commit 660cd44659a0, "drm/shmem-helper: Import dmabuf without mapping its sg_table") which skips the map/unmap cycle entirely. This means vgem no longer exercises the buggy path by default. For this reproduction, I set .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table in vgem_drv.c to use the mapping import path (one-line change, no modification to vmwgfx). The vgem change avoids the affected path for vgem specifically, but the underlying vmwgfx defect is unresolved: vmw_gem_object_get_sg_table() still returns an interior pointer when vsgt.sgt is cached on the tested kernel. The invalid kfree was demonstrated with vgem configured with gem_prime_import_sg_table; other importing drivers were not tested. I also confirmed the invalid-free on kernel 6.12.0 running on VMware Workstation, where vgem still uses the mapping import path and no vgem modification is needed: BUG: KASAN: invalid-free in dma_buf_detach+0x147/0x4e0 Free of addr ffff88811813e250 by task poc_sgtable/521 CPU: 3 UID: 65534 PID: 521 Comm: poc_sgtable Tainted: G OE 6.12.0 #3 Hardware name: VMware, Inc. VMware Virtual Platform Call Trace: kasan_report_invalid_free+0x90/0xb0 check_slab_allocation+0xf5/0x100 kfree+0xd3/0x400 dma_buf_detach+0x147/0x4e0 drm_prime_gem_destroy+0x67/0x90 [drm] drm_gem_shmem_free+0x71/0x520 [drm_shmem_helper] drm_gem_handle_delete+0xd9/0x140 [drm] Patch used for the comparison: UNFIXED means vmwgfx built from commit 6edd14dd67d76d39a518ad3bf1a98363690a5a62 (v7.3-rc4). FIXED means the same commit with the change below applied to vmw_gem_object_get_sg_table(). This patch contains two changes: (a) Add a NULL-check for bo->ttm before dereferencing it via container_of, returning -ENODEV if the TTM backend is absent. (b) Always return a freshly allocated sg_table via drm_prime_pages_to_sg() instead of returning the cached vsgt.sgt interior pointer, which is the root cause of the invalid kfree. diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c index 39f8c46550c2..98c5e42cc762 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c @@ -70,13 +70,15 @@ static void vmw_gem_object_unpin(struct drm_gem_object *obj) static struct sg_table *vmw_gem_object_get_sg_table(struct drm_gem_object *obj) { struct ttm_buffer_object *bo = drm_gem_ttm_of_gem(obj); - struct vmw_ttm_tt *vmw_tt = - container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm); + struct vmw_ttm_tt *vmw_tt; - if (vmw_tt->vsgt.sgt) - return vmw_tt->vsgt.sgt; + if (!bo->ttm) + return ERR_PTR(-ENODEV); - return drm_prime_pages_to_sg(obj->dev, vmw_tt->dma_ttm.pages, vmw_tt->dma_ttm.num_pages); + vmw_tt = container_of(bo->ttm, struct vmw_ttm_tt, dma_ttm); + + return drm_prime_pages_to_sg(obj->dev, vmw_tt->dma_ttm.pages, + vmw_tt->dma_ttm.num_pages); } static int vmw_gem_vmap(struct drm_gem_object *obj, struct iosys_map *map) IGT regression test (existing suite): I ran the igt-gpu-tools vmwgfx test suite on the same kernel (6edd14dd67d76d39a518ad3bf1a98363690a5a62, v7.3-rc4, VMware Workstation, Debian 12), swapping vmwgfx.ko built without and with the fix described above at runtime via rmmod/insmod. Results are identical -- no additional failures: IGT version: 2.6-NO-GIT (source from commit 5e43e9d, built from tarball) Invocations: sudo /usr/local/igt/vmwgfx/vmw_execution_buffer sudo /usr/local/igt/vmwgfx/vmw_mob_stress sudo /usr/local/igt/vmwgfx/vmw_ref_count sudo /usr/local/igt/vmwgfx/vmw_surface_copy sudo /usr/local/igt/vmwgfx/vmw_tri sudo /usr/local/igt/vmwgfx/vmw_prime UNFIXED FIXED vmw_execution_buffer: mob-create-map: SUCCESS SUCCESS buffer-create: SUCCESS SUCCESS execution-buffer-submit-sync: SUCCESS SUCCESS vmw_mob_stress: max_mob_mem_stress: FAIL FAIL (pre-existing) vmw_ref_count: surface_prime_transfer_explicit_mob: SUCCESS SUCCESS surface_prime_transfer_implicit_mob: SUCCESS SUCCESS surface_prime_transfer_fd_dup: SUCCESS SUCCESS surface_prime_transfer_two_surfaces: SUCCESS SUCCESS surface_prime_transfer_single_surface_multiple_handle: SUCCESS SUCCESS mob_repeated_unref: SUCCESS SUCCESS surface_repeated_unref: SUCCESS SUCCESS surface_alloc_ref_unref: SUCCESS SUCCESS surface_buffer_ref: SUCCESS SUCCESS surface_prime_refs: SUCCESS SUCCESS surface_buffer_prime_refs: SUCCESS SUCCESS vmw_surface_copy: test_invalid_copies: SUCCESS SUCCESS test_invalid_copies_3d: SUCCESS SUCCESS vmw_tri: tri: FAIL FAIL (pre-existing) tri-no-sync-coherent: FAIL FAIL (pre-existing) tri-2d: SUCCESS SUCCESS vmw_prime: basic-vgem: SUCCESS SUCCESS tri-map-gem: FAIL FAIL (pre-existing) tri-map-dmabuf: FAIL FAIL (pre-existing) draw-dumb-buffer: FAIL FAIL (pre-existing) buffer-surface-fb-sharing-sync-readback: FAIL FAIL (pre-existing) buffer-surface-fb-sharing-sync: FAIL FAIL (pre-existing) buffer-surface-fb-sharing: FAIL FAIL (pre-existing) 18 SUCCESS, 9 FAIL (all pre-existing, identical in both runs). Every subtest listed individually. No additional failures from the fix. IGT regression test (new sgtable-invalid-free subtest): I also wrote and compiled a dedicated IGT subtest (vmw_prime_sgtable) that exercises the same PRIME export/import/close flow from the standalone reproducer. It was compiled against the igt-gpu-tools tree and executed on the same kernel (v7.3-rc4, VMware Workstation, CONFIG_KASAN=y), with the same vgem mapping-import configuration described above. Results: - UNFIXED vmwgfx: subtest sgtable-invalid-free SUCCESS. Immediately afterward, a TTM cleanup worker Oopsed in dma_direct_unmap_sg, with vmw_ttm_unmap_dma in the call trace. This is consistent with corruption of the sg_table used during cleanup. - FIXED vmwgfx: subtest sgtable-invalid-free SUCCESS. dmesg after the test shows no Oops, no KASAN reports, no BUG. A separate WARNING from vmw_cmdbuf_ctx_process (command buffer error during EXECBUF) appears in dmesg; it is distinct from the sg_table invalid-free reported here. Full IGT logs follow in two replies to this message (unfixed and fixed runs). Standalone reproducer results: The standalone reproducer (vmw_sgtable_test.c) was compiled and executed on VMware Workstation 26.0.0, kernel commit 6edd14dd67d76d39a518ad3bf1a98363690a5a62 (v7.3-rc4), without and with the fix described above: - UNFIXED vmwgfx: BUG: KASAN: invalid-free in dma_buf_unmap_attachment - FIXED vmwgfx: clean (no KASAN) Both the standalone reproducer and the IGT testcase were compiled and executed as described above. The standalone reproducer (vmw_sgtable_test.c) and IGT testcase source (vmw_prime_sgtable.c) are included below. Full IGT logs follow in two replies to this message (unfixed and fixed runs). Requires vgem with gem_prime_import_sg_table (one-line override in vgem_drv.c, see note above) to exercise the mapping import path. Let me know if you can reproduce with this setup, or if you need anything else from my side. I am happy to send v2 patches whenever you are ready. thanks, Aldo ---8<--- vmw_sgtable_test.c ---8<--- /* * vmw_prime_sgtable_test: Reproduce embedded sg_table invalid-free in vmwgfx. * Without fix: KASAN reports invalid-free. With fix: clean. */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <errno.h> #include <stdint.h> /* DRM core */ #define DRM_COMMAND_BASE 0x40 #define DRM_IOCTL_BASE 'd' #define DRM_IOWR(nr, type) _IOWR(DRM_IOCTL_BASE, nr, type) #define DRM_IOW(nr, type) _IOW(DRM_IOCTL_BASE, nr, type) struct drm_prime_handle { uint32_t handle; uint32_t flags; int32_t fd; }; struct drm_gem_close { uint32_t handle; uint32_t pad; }; #define DRM_IOCTL_PRIME_HANDLE_TO_FD _IOWR(DRM_IOCTL_BASE, 0x2d, struct drm_prime_handle) #define DRM_IOCTL_PRIME_FD_TO_HANDLE _IOWR(DRM_IOCTL_BASE, 0x2e, struct drm_prime_handle) #define DRM_IOCTL_GEM_CLOSE _IOW(DRM_IOCTL_BASE, 0x09, struct drm_gem_close) /* vmwgfx */ #define DRM_VMW_ALLOC_DMABUF 1 #define DRM_VMW_GB_SURFACE_CREATE 23 #define DRM_VMW_GB_SURFACE_CREATE_EXT 27 #define DRM_VMW_EXECBUF 12 #define DRM_VMW_EXECBUF_VERSION 2 #define SVGA_3D_CMD_BIND_GB_SURFACE 1099 struct drm_vmw_alloc_bo_req { uint32_t size, pad64; }; struct drm_vmw_bo_rep { uint64_t map_handle; uint32_t handle, cur_gmr_id, cur_gmr_offset, pad64; }; union drm_vmw_alloc_bo_arg { struct drm_vmw_alloc_bo_req req; struct drm_vmw_bo_rep rep; }; struct drm_vmw_size { uint32_t width, height, depth, pad64; }; struct drm_vmw_gb_surface_create_req { uint32_t svga3d_flags; uint32_t format; uint32_t mip_levels; uint32_t drm_surface_flags; uint32_t multisample_count; uint32_t autogen_filter; uint32_t array_size; uint32_t buffer_handle; struct drm_vmw_size base_size; }; struct drm_vmw_gb_surface_create_rep { uint32_t handle; uint32_t backup_size; uint32_t buffer_handle; uint32_t buffer_size; uint64_t buffer_map_handle; }; union drm_vmw_gb_surface_create_arg { struct drm_vmw_gb_surface_create_rep rep; struct drm_vmw_gb_surface_create_req req; }; struct drm_vmw_gb_surface_create_ext_req { struct drm_vmw_gb_surface_create_req base; uint32_t version; uint32_t svga3d_flags_upper_32_bits; uint32_t multisample_pattern; uint32_t quality_level; uint32_t buffer_byte_stride; uint32_t must_be_zero; }; union drm_vmw_gb_surface_create_ext_arg { struct drm_vmw_gb_surface_create_ext_req req; struct drm_vmw_gb_surface_create_rep rep; }; struct drm_vmw_execbuf_arg { uint64_t commands; uint32_t command_size; uint32_t throttle_us; uint64_t fence_rep; uint32_t version; uint32_t flags; uint32_t context_handle; int32_t imported_fence_fd; }; #pragma pack(push, 1) typedef struct { uint32_t id; uint32_t size; } SVGA3dCmdHeader; typedef struct { uint32_t sid; uint32_t mobid; } SVGA3dCmdBindGBSurface; #pragma pack(pop) #define IOCTL_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_ALLOC_DMABUF, union drm_vmw_alloc_bo_arg) #define IOCTL_SURFACE_EXT DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GB_SURFACE_CREATE_EXT, union drm_vmw_gb_surface_create_ext_arg) #define IOCTL_EXECBUF DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, struct drm_vmw_execbuf_arg) int main(void) { int vmw_fd, vgem_fd, prime_fd, ret; union drm_vmw_alloc_bo_arg alloc; union drm_vmw_gb_surface_create_ext_arg surf; struct drm_vmw_execbuf_arg exec; struct { SVGA3dCmdHeader hdr; SVGA3dCmdBindGBSurface body; } cmd; struct drm_prime_handle ph, ph2; struct drm_gem_close cl; void *map; vmw_fd = open("/dev/dri/renderD128", O_RDWR); vgem_fd = open("/dev/dri/renderD129", O_RDWR); if (vmw_fd < 0 || vgem_fd < 0) { perror("open"); return 77; } /* 1. Alloc BO */ memset(&alloc, 0, sizeof(alloc)); alloc.req.size = 256 * 256 * 4; ret = ioctl(vmw_fd, IOCTL_ALLOC, &alloc); if (ret < 0) { perror("alloc"); return 1; } printf("[+] BO handle=%u\n", alloc.rep.handle); /* 2. Populate */ map = mmap(NULL, 256*256*4, PROT_READ|PROT_WRITE, MAP_SHARED, vmw_fd, alloc.rep.map_handle); if (map != MAP_FAILED) { memset(map, 0x41, 256*256*4); munmap(map, 256*256*4); } /* 3. PRIME export the MOB handle */ memset(&ph, 0, sizeof(ph)); ph.handle = alloc.rep.handle; ph.flags = O_CLOEXEC | O_RDWR; ret = ioctl(vmw_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &ph); if (ret < 0) { perror("export"); return 1; } prime_fd = ph.fd; printf("[+] PRIME exported fd=%d\n", prime_fd); /* 4. Create GB surface + bind (triggers vmw_ttm_map_dma -> vsgt.sgt cache) */ memset(&surf, 0, sizeof(surf)); surf.req.base.svga3d_flags = (1 << 6); /* SVGA3D_SURFACE_HINT_RENDERTARGET */ surf.req.base.format = 37; /* SVGA3D_BUFFER */ surf.req.base.mip_levels = 1; surf.req.base.autogen_filter = 1; surf.req.base.array_size = 1; surf.req.base.drm_surface_flags = 1; /* drm_vmw_surface_flag_shareable */ surf.req.base.buffer_handle = alloc.rep.handle; /* backup = our MOB */ surf.req.base.base_size.width = 64; surf.req.base.base_size.height = 64; surf.req.base.base_size.depth = 1; surf.req.version = 1; /* drm_vmw_surface_version_v1 */ ret = ioctl(vmw_fd, IOCTL_SURFACE_EXT, &surf); if (ret < 0) { printf("[-] surface create: %s (non-fatal)\n", strerror(errno)); } else { printf("[+] surface sid=%u backup_size=%u\n", surf.rep.handle, surf.rep.backup_size); /* EXECBUF bind */ cmd.hdr.id = SVGA_3D_CMD_BIND_GB_SURFACE; cmd.hdr.size = sizeof(cmd.body); cmd.body.sid = surf.rep.handle; cmd.body.mobid = alloc.rep.handle; memset(&exec, 0, sizeof(exec)); exec.commands = (uint64_t)(uintptr_t)&cmd; exec.command_size = sizeof(cmd); exec.version = DRM_VMW_EXECBUF_VERSION; exec.context_handle = 0xFFFFFFFF; /* SVGA3D_INVALID_ID = no DX context */ ret = ioctl(vmw_fd, IOCTL_EXECBUF, &exec); if (ret < 0) printf("[-] execbuf bind: %s\n", strerror(errno)); else printf("[+] BIND_GB_SURFACE OK\n"); } /* 5. Cross-device PRIME import */ memset(&ph2, 0, sizeof(ph2)); ph2.fd = prime_fd; ret = ioctl(vgem_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &ph2); if (ret < 0) { printf("[-] PRIME import failed: errno=%d\n", errno); close(prime_fd); close(vmw_fd); close(vgem_fd); return 1; } printf("[+] PRIME imported handle=%u\n", ph2.handle); /* 6. Close import -> kfree(sgt) */ memset(&cl, 0, sizeof(cl)); cl.handle = ph2.handle; ioctl(vgem_fd, DRM_IOCTL_GEM_CLOSE, &cl); close(prime_fd); printf("[+] DONE. Check: sudo dmesg | grep KASAN\n"); close(vmw_fd); close(vgem_fd); return 0; } ---8<--- vmw_prime_sgtable.c (IGT testcase) ---8<--- // SPDX-License-Identifier: GPL-2.0 OR MIT /* * Test: vmw_prime_sgtable * * Validates that vmwgfx correctly handles embedded sg_table lifetime * during cross-device PRIME import/close sequences. On unfixed kernels, * closing the imported GEM handle triggers kfree() on the embedded * (non-heap-allocated) sg_table inside struct vmw_ttm_tt, producing a * KASAN invalid-free splat. */ #include "igt_kms.h" #include "igt_vmwgfx.h" #include <fcntl.h> #include <string.h> #include <sys/ioctl.h> IGT_TEST_DESCRIPTION("Test sg_table lifetime in vmwgfx PRIME export/import paths."); /* * Full ioctl number for DRM_VMW_EXECBUF — vmwgfx_drm.h provides the * command offset but not the composed ioctl macro. */ #define IOCTL_VMW_EXECBUF \ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, struct drm_vmw_execbuf_arg) static void test_sgtable_invalid_free(int vmw_fd, int import_fd) { struct vmw_mob *mob; struct vmw_surface *surf; int prime_fd, ret; void *map; const uint32_t bo_size = 64 * 64 * 4; SVGA3dSize surf_size = { .width = 64, .height = 64, .depth = 1 }; /* 1. Create and populate a MOB */ mob = vmw_ioctl_mob_create(vmw_fd, bo_size); igt_require(mob); igt_require(mob->handle != 0); map = vmw_ioctl_mob_map(vmw_fd, mob); igt_require(map); memset(map, 0x41, bo_size); vmw_ioctl_mob_unmap(mob); /* 2. PRIME export the MOB handle */ prime_fd = prime_handle_to_fd(vmw_fd, mob->handle); igt_assert(prime_fd >= 0); /* 3. Create a GB surface backed by this MOB */ surf = vmw_ioctl_create_surface_full(vmw_fd, SVGA3D_SURFACE_HINT_RENDERTARGET, /* flags */ SVGA3D_BUFFER, /* format */ 0, /* multisample_count */ SVGA3D_MS_PATTERN_NONE, SVGA3D_MS_QUALITY_NONE, SVGA3D_TEX_FILTER_NEAREST, /* autogen_filter */ 1, /* num_mip_levels */ 1, /* array_size */ surf_size, mob->handle, /* buffer_handle (backup) */ drm_vmw_surface_flag_shareable); /* Surface creation + bind trigger DMA mapping of the BO. */ if (surf) { /* 4. Bind surface to MOB via raw EXECBUF */ struct { SVGA3dCmdHeader hdr; SVGA3dCmdBindGBSurface body; } cmd; struct drm_vmw_execbuf_arg exec; cmd.hdr.id = SVGA_3D_CMD_BIND_GB_SURFACE; cmd.hdr.size = sizeof(cmd.body); cmd.body.sid = surf->base.handle; cmd.body.mobid = mob->handle; memset(&exec, 0, sizeof(exec)); exec.commands = (uint64_t)(uintptr_t)&cmd; exec.command_size = sizeof(cmd); exec.version = DRM_VMW_EXECBUF_VERSION; exec.context_handle = 0xFFFFFFFF; ret = ioctl(vmw_fd, IOCTL_VMW_EXECBUF, &exec); if (ret < 0) igt_debug("execbuf bind: %s (non-fatal)\n", strerror(errno)); else igt_debug("BIND_GB_SURFACE OK (sid=%u, mobid=%u)\n", surf->base.handle, mob->handle); } /* * 5. Cross-device PRIME import using raw ioctl. * * Do NOT use prime_fd_to_handle() — it returns ENOSYS on * some vmwgfx setups. Raw DRM_IOCTL_PRIME_FD_TO_HANDLE works. */ { struct drm_prime_handle import_args; struct drm_gem_close close_args; memset(&import_args, 0, sizeof(import_args)); import_args.fd = prime_fd; ret = ioctl(import_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &import_args); igt_assert_eq(ret, 0); igt_assert(import_args.handle != 0); igt_debug("PRIME imported handle=%u\n", import_args.handle); /* * 6. Close the imported handle. * * On unfixed kernels this triggers kfree() on the * embedded sg_table that was never separately allocated, * causing KASAN invalid-free. On fixed kernels this * completes cleanly. */ memset(&close_args, 0, sizeof(close_args)); close_args.handle = import_args.handle; ret = ioctl(import_fd, DRM_IOCTL_GEM_CLOSE, &close_args); igt_assert_eq(ret, 0); } /* 7. Cleanup */ close(prime_fd); if (surf) vmw_ioctl_surface_unref(vmw_fd, surf); vmw_ioctl_mob_close_handle(vmw_fd, mob); } int igt_main() { int vmw_fd = -1; int import_fd = -1; igt_fixture() { vmw_fd = open("/dev/dri/renderD128", O_RDWR); igt_require(vmw_fd >= 0); /* * Need a second DRM device for cross-device PRIME import. * Try renderD129 (typically VGEM or another GPU node). */ import_fd = open("/dev/dri/renderD129", O_RDWR); if (import_fd < 0) import_fd = open("/dev/dri/card1", O_RDWR); igt_require_f(import_fd >= 0, "Need a second DRM device for PRIME import\n"); } igt_describe("Validates sg_table lifetime during PRIME" " export/import/close. On unfixed kernels, closing the" " imported handle triggers an invalid kfree of the" " embedded sg_table."); igt_subtest("sgtable-invalid-free") { test_sgtable_invalid_free(vmw_fd, import_fd); } igt_fixture() { if (import_fd >= 0) close(import_fd); if (vmw_fd >= 0) close(vmw_fd); } }
