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);
        }
}

Reply via email to