Hello,

I'm reporting an out-of-bounds read in the vmwgfx cursor-snoop path that is
reachable by any local unprivileged user via the DRM render node
(/dev/dri/renderD128, mode 0666 on stock installs). It reliably faults the
kernel (oops; a full panic when panic_on_oops=1, which is the default on
RHEL and derivatives). It is present in current mainline (torvalds/master,
HEAD b643e495ae92 as of 2026-08-09) and in all shipping stable/distro
kernels I checked.

I tell This is an incomplete-fix variant of CVE-2022-36280. Commit
4cf949c7fafe ("drm/vmwgfx: Validate the box size for the snooped cursor")
bounded the copybox width/height (the destination side), but left
cmd->dma.guest.pitch (the source-side stride) unbounded.

This report is not public. I'm happy to follow the kernel security process
and can provide the full PoC source on request.

*Affected code*

drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c : vmw_kms_cursor_snoop()
(historically drivers/gpu/drm/vmwgfx/vmwgfx_kms.c)

The source buffer is kmapped for a FIXED number of pages sized to the
snooped cursor (64 * image_pitch = 4 pages):

    kmap_num = (VMW_CURSOR_SNOOP_HEIGHT * image_pitch) >> PAGE_SHIFT;

    ...

    ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);

    virtual = ttm_kmap_obj_virtual(&map, &is_iomem);



but the slow-path copy reads at an attacker-controlled stride,

cmd->dma.guest.pitch, with no bound:

    if (box->w == VMW_CURSOR_SNOOP_WIDTH && cmd->dma.guest.pitch ==
image_pitch) {

        memcpy(srf->snooper.image, virtual, VMW_CURSOR_SNOOP_HEIGHT *
image_pitch);

    } else {

        for (i = 0; i < box->h; i++)                    /* box->h <= 64 */

            memcpy(srf->snooper.image + i * image_pitch,

                   virtual + i * cmd->dma.guest.pitch,   /* guest.pitch
UNBOUNDED */

                   box->w * desc->pitchBytesPerBlock);

    }



The validation block above this checks the box geometry (x/y/z,
srcx/srcy/srcz, d, box_count, w <= 64, h <= 64) and the guest offset
alignment, but never checks guest.pitch.

The equality test on the fast/slow branch (guest.pitch == image_pitch) only
*selects* the code path; a mismatching pitch falls straight into the
unbounded slow path. With, e.g., guest.pitch = 0x10000000 and box->h = 64,
iteration i=1 already reads 256 MB past the 4-page source mapping.

*Reachability (unprivileged, default config)*

·         /dev/dri/renderD128 is world-accessible (0666) on stock systemd
installs.

·         The trigger ioctls are all DRM_RENDER_ALLOW:

o   DRM_VMW_ALLOC_DMABUF   (source bo)

o   DRM_VMW_CREATE_SURFACE (allocates srf->snooper.image for a 64x64
A8R8G8B8 scanout surface via vmw_cursor_snooper_create)

o   DRM_VMW_EXECBUF        (carries SVGA3dCmdSurfaceDMA ->  vmw_cmd_dma()
-> vmw_kms_cursor_snoop())

·         No capabilities, user namespaces, or races are required.

·          Affects any Linux guest running vmwgfx (VMware and VirtualBox
VMSVGA).

*Impact*

Reliable unprivileged local denial of service. The OOB read faults on
unmapped memory and oopses the kernel; with panic_on_oops=1 (default on
RHEL/derivatives and common on hardened/server configs) it is a full kernel
panic / machine-down.

Escalation assessment (for completeness): there is no OOB *write* here –
the destination srf->snooper.image is bounded by the already-validated
box->w/h.

The snooped buffer is only forwarded to the hypervisor host on a subsequent
DRM-master cursor-plane update (vmw_cursor_plane_update_legacy ->
vmw_send_define_cursor_cmd), so an unprivileged process cannot read the
leaked bytes back.

A carefully tuned, non-faulting read could constitute a limited guest->host
kernel-memory disclosure if a privileged cursor update later occurs, but
the reliable, demonstrated outcome is the DoS.

*Reproduction*

Confirmed three ways:

·         source analysis;

·         a KASAN QEMU lab (qemu -vga vmware);

·         a stock CentOS Stream 9 guest (kernel 5.14.0-731.el9) under
VirtualBox, run as an ordinary uid=1000 user.

Unprivileged PoC:

1.      fd = open("/dev/dri/renderD128", O_RDWR)

2.      DRM_VMW_CREATE_SURFACE: scanout=1, 1 mip level, size 64x64x1,
format SVGA3D_A8R8G8B8            -> allocates srf->snooper.image

3.      DRM_VMW_ALLOC_DMABUF: size >= 4 pages   -> source bo (handle H)

4.      DRM_VMW_EXECBUF, one SVGA3dCmdSurfaceDMA (SVGA_3D_CMD_SURFACE_DMA):

       host  = { sid = surface, face = 0, mipmap = 0 }

       guest = { ptr = { gmrId = H, offset = 0 }, pitch = 0x10000000 }

       one SVGA3dCopyBox { w = 64, h = 64, d = 1, all others 0 }

       + SVGA3dCmdSurfaceDMASuffix { suffixSize = 12, ... }



Observed oops (stock 5.14.0-731.el9, uid 1000, VirtualBox VMSVGA):

  BUG: unable to handle page fault for address: ffffd1a050a13000

  #PF: supervisor read access in kernel mode

  CPU: 0 PID: 3750 Comm: vmwpoc  5.14.0-731.el9.x86_64

  RIP: 0010:memcpy+0x12/0x20

  RSI: ffffd1a050a13000  RBP: ffffd1a040a13000    (RSI - RBP = 0x10000000 =
i*guest.pitch)

  Call Trace:

   vmw_kms_cursor_snoop+0x174/0x2c0 [vmwgfx]

   vmw_cmd_dma+0xdf/0x170 [vmwgfx]

   vmw_cmd_check+0xb1/0x240 [vmwgfx]

   vmw_execbuf_process+0x4eb/0xdd0 [vmwgfx]

   vmw_execbuf_ioctl+0xf4/0x170 [vmwgfx]

   drm_ioctl_kernel / drm_ioctl / __x64_sys_ioctl / do_syscall_64



*Reporter*

Youness HFA from AGBS Pentest Team <[email protected]>

Happy to coordinate disclosure timing and CVE assignment per your process.

Reply via email to