Hello,
This is a follow-up to my previous disclosure email regarding the integer
overflow vulnerability in vmw_surface_define_ioctl() (vmwgfx_surface.c:794),
sent earlier today.
I am writing to address two points:
1. ADDITIONAL TECHNICAL DETAILS
1.1 — Root Cause (deeper analysis)
The vulnerability resides in the two nested loops inside
vmw_surface_define_ioctl():
cur_bo_offset = 0; /* uint32_t */
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; i++) {
for (j = 0; j < metadata->mip_levels[i]; j++) {
cur_offset->bo_offset = cur_bo_offset;
cur_bo_offset += vmw_surface_get_image_buffer_size( /* line 794 */
desc, cur_size, stride); /* NO overflow check on
accumulation */
}
}
res->guest_memory_size = cur_bo_offset; /* wrapped value stored here */
While vmw_surface_get_image_buffer_size() internally uses clamped_umul32 to
protect individual per-mip-level calculations, the outer accumulation
across up to 144 iterations (6 faces × 24 mip levels) performs a plain
uint32_t += without any call to check_add_overflow(). This is the exact
pattern that CWE-190 describes.
The wrapped value is then passed directly to:
vmw_bo_create_and_populate(dev_priv, res->guest_memory_size, ...)
which allocates the backing buffer object (BO) with the undersized value.
The surface metadata (srf->offsets[].bo_offset), however, retains the
correct large values calculated before the wrap — creating the mismatch.
1.2 — Trigger Parameters
Minimal reproducer:
Format: 40 (SVGA3D_A16B16G16R16, 8 bytes/pixel)
Width: 8192
Faces: DRM_VMW_MAX_SURFACE_FACES (6)
Mip levels: DRM_VMW_MAX_MIP_LEVELS (24) per face
Arithmetic:
Per-face sum = 715,827,960 bytes
6faces total = 4,294,967,760 bytes (u64)
Wrapped (u32) = 464 bytes <-- guest_memory_size
BO allocated = 464 bytes
TTM page alloc = 4,096 bytes (PAGE_SIZE minimum)
The kernel accepts the ioctl without returning an error and issues a
valid surface SID (confirmed: SID=53036 in testing).
1.3 — Crash Path (VMware Workstation)
When EXECBUF SURFACE_DMA is issued referencing this surface:
1. vmw_cmd_dma() calls vmw_cmd_res_check() to validate the surface
2. vmw_resource_validate() triggers the surface bind
3. The host (VMware) computes the MOB (Memory Object Buffer) size
from the surface dimensions: 4.29 GB => 1,048,576 pages
4. vmw_mob_pt_setup() attempts to build a 3-level page table
for 1,048,576 pages
5. BUG_ON(mob->pt_level > 2) fires in vmwgfx_mob.c:518
6. Kernel panic — full system crash from unprivileged userspace
Kernel log (observed):
kernel BUG at drivers/gpu/drm/vmwgfx/vmwgfx_mob.c:518!
Oops: invalid opcode: 0000 [#1] SMP NOPTI
RIP: vmw_mob_pt_setup+0x2b5/0x3b0 [vmwgfx]
Call Trace:
vmw_execbuf_ioctl
drm_ioctl
__x64_sys_ioctl
1.4 — Distinction from CVE-2017-7294
CVE-2017-7294 addressed a different integer overflow in the same function
(vmw_surface_define_ioctl), triggered by the levels data. That fix added
validation on the mip_levels values themselves. The current vulnerability
is in the accumulation of cur_bo_offset across mip iterations — a distinct
code path that was not covered by the 2017 patch, allowing the bug to
survive all subsequent kernel versions up to 7.0.12.
1.5 — Proposed Fix
Replace the unchecked accumulation at line 794:
/* BEFORE (vulnerable): */
cur_bo_offset += vmw_surface_get_image_buffer_size(desc, cur_size, stride);
/* AFTER (fixed): */
u32 img_size = vmw_surface_get_image_buffer_size(desc, cur_size, stride);
if (check_add_overflow(cur_bo_offset, img_size, &cur_bo_offset)) {
ret = -EINVAL;
goto out_invalid_size;
}
This is consistent with the existing use of check_add_overflow() throughout
the rest of the vmwgfx driver (e.g., vmwgfx_execbuf.c, vmwgfx_bo.c).
1.6 — CVSS 3.1 Breakdown
Attack Vector (AV): Local (L)
-> Requires local account + membership in 'video' group
Attack Complexity (AC): Low (L)
-> No race condition; single ioctl call is sufficient
Privileges Required (PR): Low (L)
-> 'video' group membership, not root
User Interaction (UI): None (N)
Scope (S): Unchanged (U)
Confidentiality (C): None (N) — DoS only in tested environment
Integrity (I): None (N)
Availability (A): High (H) — kernel panic confirmed
Base Score: 5.5 (Medium)
Note: If OOB write is achieved in environments without the
BUG_ON(pt_level > 2) guard (e.g., older vmwgfx or ESXi without
the 2-level PT limit), the Integrity and Confidentiality scores
would increase, pushing the score toward High (7.8+).
The attached PoC (poc_vmwgfx_bug6_final.c) from the original email
remains valid and unchanged.
We remain available for any additional questions or testing.
Best regards,
Fernando Maia
Security Researcher
Renan Malafatti
Security Researcher
________________________________
De: Fernando Gomes
Enviado: quinta-feira, 9 de julho de 2026 17:28
Para: [email protected] <[email protected]>;
[email protected] <[email protected]>
Assunto: [SECURITY] drm/vmwgfx: Integer overflow in cur_bo_offset accumulation
in vmw_surface_define_ioctl()
Hello,
I am reporting a security vulnerability discovered during a kernel security
audit of Linux 7.0.12+kali-rt-amd64. This report is submitted under coordinated
disclosure.
Vulnerability Summary
An integer overflow exists in the cur_bo_offset accumulation loop inside
vmw_surface_define_ioctl() in drivers/gpu/drm/vmwgfx/vmwgfx_surface.c, line
794. The loop iterates up to 144 times (DRM_VMW_MAX_SURFACE_FACES ×
DRM_VMW_MAX_MIP_LEVELS) accumulating per-mip-level buffer sizes into a uint32_t
variable without any overflow check. The resulting wrapped value is then used
as guest_memory_size to allocate the backing buffer object (BO), creating a
severe size discrepancy between the allocated BO and the surface the GPU
expects to access.
Affected Component
* File: drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
* Function: vmw_surface_define_ioctl()
* Line: 794
* Kernel tested: Linux 7.0.12+kali-rt-amd64
Vulnerable Code
c
cur_bo_offset = 0; /* uint32_t — no overflow protection */
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
for (j = 0; j < metadata->mip_levels[i]; ++j) {
cur_offset->bo_offset = cur_bo_offset;
cur_bo_offset += vmw_surface_get_image_buffer_size( /* line 794 */
desc, cur_size, stride); /* NO check_add_overflow() */
}
}
res->guest_memory_size = cur_bo_offset; /* stored wrapped value */
Proof of Concept
Using format 40 (SVGA3D_A16_B16_G16_R16, 8 bytes/pixel), width=8192, 6 faces
and 24 mip levels:
* Real surface size (u64): 4,294,967,760 bytes (~4.29 GB)
* Wrapped guest_memory_size (u32): 464 bytes
* BO allocated: 464 bytes
* Surface SID returned by kernel: 53036 (no error returned)
When EXECBUF SURFACE_DMA is subsequently issued, the host attempts to construct
a 3-level MOB page table for the 4.29 GB surface. This exceeds vmwgfx's 2-level
limit, triggering BUG_ON(mob->pt_level > 2) in vmwgfx_mob.c:518 and causing an
immediate kernel panic from an unprivileged process in the video group.
Impact
* Denial of Service (kernel panic) — confirmed on VMware Workstation Pro
* Potential out-of-bounds write in environments without the pt_level guard
(e.g., ESXi/vSphere)
* Access vector: local user in the video group, no root privileges required
Suggested Fix
c
u32 img_size = vmw_surface_get_image_buffer_size(desc, cur_size, stride);
if (check_add_overflow(cur_bo_offset, img_size, &cur_bo_offset)) {
ret = -EINVAL;
goto out_invalid_size;
}
Related CVEs
* CVE-2017-7294 — Prior integer overflow in the same function
(vmw_surface_define_ioctl), fixed in kernel 4.10.6. The current bug is a
distinct variant in the accumulation path that was not addressed by that fix.
* CVE-2025-40277 — Recent integer overflow in vmw_cmd_check (vmwgfx).
Attachments
* poc_vmwgfx_bug6_final.c — self-contained PoC; compile with gcc -O0 -o poc
poc_vmwgfx_bug6_final.c and run as a user in the video group.
I am available to provide additional technical details, logs, or testing
assistance. I request coordinated disclosure with a standard 90-day embargo.
Regards,
Fernando Maia and Renan Malafati
Security Researcher's