* [PATCH] drm/vmwgfx: fix surface size overflow, execbuf bounds, and cursor/msg leaks
@ 2026-09-19 22:34 Hui Peng
0 siblings, 0 replies; only message in thread
From: Hui Peng @ 2026-09-19 22:34 UTC (permalink / raw)
To: zack.rusin, airlied, tzimmermann, simona
Cc: bcm-kernel-feedback-list, dri-devel, linux-kernel
Fix multiple integer overflows, out-of-bounds accesses, and resource
leaks across drivers/gpu/drm/vmwgfx/ (vmwgfx_surface.c,
vmwgfx_execbuf.c, vmwgfx_msg.c, vmwgfx_fence.c, vmwgfx_cursor_plane.c,
vmwgfx_cotable.c, vmwgfx_bo.c, and vmw_surface_cache.h).
Fixes: fb1d9738ca05 ("drm/vmwgfx: Add DRM driver for VMware Virtual GPU")
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@gmail.com>
---
diff --git a/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h b/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h
index 1ac3cb151b11..bc5f7facf728 100644
--- a/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h
+++ b/drivers/gpu/drm/vmwgfx/vmw_surface_cache.h
@@ -100,7 +100,7 @@ vmw_surface_calculate_pitch(const SVGA3dSurfaceDesc *desc,
vmw_surface_get_size_in_blocks(desc, size, &blocks);
- pitch = blocks.width * desc->pitchBytesPerBlock;
+ pitch = clamped_umul32(blocks.width, desc->pitchBytesPerBlock);
return pitch;
}
@@ -159,11 +159,13 @@ vmw_surface_get_serialized_size(SVGA3dSurfaceFormat format,
for (mip = 0; mip < num_mip_levels; mip++) {
struct drm_vmw_size size =
vmw_surface_get_mip_size(base_level_size, mip);
- total_size += vmw_surface_get_image_buffer_size(desc,
- &size, 0);
+ total_size = min_t(u64,
+ (u64)total_size +
+ vmw_surface_get_image_buffer_size(desc, &size, 0),
+ U32_MAX);
}
- return total_size * num_layers;
+ return clamped_umul32(total_size, num_layers);
}
/**
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
index 9c7a73c0b0dc..53b3ecfc476b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
@@ -535,7 +535,8 @@ static int vmw_user_bo_synccpu_release(struct drm_file *filp,
if (!ret) {
if (!(flags & drm_vmw_synccpu_allow_cs)) {
- atomic_dec(&vmw_bo->cpu_writers);
+ if (!atomic_add_unless(&vmw_bo->cpu_writers, -1, 0))
+ ret = -EINVAL;
}
vmw_user_bo_unref(&vmw_bo);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c
index 091f1039a052..4273be7ac1ac 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cotable.c
@@ -481,6 +481,10 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size)
goto out_wait;
}
+ ret = dma_resv_reserve_fences(bo->base.resv, 1);
+ if (unlikely(ret))
+ goto out_wait;
+
vmw_resource_mob_detach(res);
res->guest_memory_bo = buf;
res->guest_memory_size = new_size;
@@ -505,10 +509,6 @@ static int vmw_cotable_resize(struct vmw_resource *res, size_t new_size)
vmw_user_bo_unref(&old_buf);
res->id = vcotbl->type;
- ret = dma_resv_reserve_fences(bo->base.resv, 1);
- if (unlikely(ret))
- goto out_wait;
-
/* Release the pin acquired in vmw_bo_create */
ttm_bo_unpin(bo);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
index d1e7df500190..e6776ed6cfd4 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c
@@ -116,6 +116,12 @@ static void vmw_cursor_update_mob(struct vmw_private *vmw,
u32 *image = vmw_bo_map_and_cache(bo);
const u32 image_size = vps->base.crtc_w * vps->base.crtc_h * sizeof(*image);
+ if (!bo || !vps->cursor.mob)
+ return;
+ if (image_size > bo->tbo.base.size ||
+ sizeof(*header) + image_size > vps->cursor.mob->tbo.base.size)
+ return;
+
header = vmw_bo_map_and_cache(vps->cursor.mob);
alpha_header = &header->header.alphaHeader;
@@ -359,7 +365,9 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
box->x != 0 || box->y != 0 || box->z != 0 ||
box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
box->d != 1 || box_count != 1 ||
- box->w > VMW_CURSOR_SNOOP_WIDTH || box->h > VMW_CURSOR_SNOOP_HEIGHT) {
+ box->w == 0 || box->h == 0 ||
+ box->w > VMW_CURSOR_SNOOP_WIDTH || box->h > VMW_CURSOR_SNOOP_HEIGHT ||
+ cmd->dma.guest.pitch > image_pitch) {
/* TODO handle none page aligned offsets */
/* TODO handle more dst & src != 0 */
/* TODO handle more then one copy */
@@ -375,6 +383,9 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
kmap_num = (VMW_CURSOR_SNOOP_HEIGHT * image_pitch) >> PAGE_SHIFT;
+ if (bo->base.size < ((kmap_offset + kmap_num) << PAGE_SHIFT))
+ return;
+
ret = ttm_bo_reserve(bo, true, false, NULL);
if (unlikely(ret != 0)) {
DRM_ERROR("reserve failed\n");
@@ -393,7 +404,7 @@ void vmw_kms_cursor_snoop(struct vmw_surface *srf,
} else {
/* Image is unsigned pointer. */
for (i = 0; i < box->h; i++)
- memcpy(srf->snooper.image + i * image_pitch,
+ memcpy((u8 *)srf->snooper.image + i * image_pitch,
virtual + i * cmd->dma.guest.pitch,
box->w * desc->pitchBytesPerBlock);
}
@@ -548,7 +559,9 @@ vmw_cursor_buffer_changed(struct vmw_plane_state *new_vps,
old_image = vmw_bo_map_and_cache(old_bo);
new_image = vmw_bo_map_and_cache(new_bo);
- if (old_image && new_image && old_image != new_image)
+ if (old_image && new_image && old_image != new_image &&
+ size <= old_bo->tbo.base.size &&
+ size <= new_bo->tbo.base.size)
changed = memcmp(old_image, new_image, size) !=
0;
@@ -728,6 +741,14 @@ int vmw_cursor_plane_atomic_check(struct drm_plane *plane,
if (!fb)
return 0;
+ if (new_state->crtc_w <= 0 || new_state->crtc_h <= 0 ||
+ new_state->crtc_w > vmw->fb_max_width ||
+ new_state->crtc_h > vmw->fb_max_height) {
+ drm_warn(&vmw->drm, "Invalid cursor dimensions (%d, %d)\n",
+ new_state->crtc_w, new_state->crtc_h);
+ return -EINVAL;
+ }
+
update_type = vmw_cursor_update_type(vmw, vps);
if (update_type == VMW_CURSOR_UPDATE_LEGACY) {
if (new_state->crtc_w != VMW_CURSOR_SNOOP_WIDTH ||
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index a9136a6523cb..92c12d6e98d6 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -1511,6 +1511,10 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
cmd = container_of(header, typeof(*cmd), header);
+ if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) {
+ VMW_DEBUG_USER("Invalid DMA command size.\n");
+ return -EINVAL;
+ }
if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) {
VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n");
return -EINVAL;
@@ -1572,6 +1576,10 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv,
VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDrawPrimitives);
SVGA3dVertexDecl *decl = (SVGA3dVertexDecl *)(
(unsigned long)header + sizeof(*cmd));
+ if (unlikely(header->size < sizeof(cmd->body))) {
+ VMW_DEBUG_USER("Draw command size too small.\n");
+ return -EINVAL;
+ }
SVGA3dPrimitiveRange *range;
uint32_t i;
uint32_t maxnum;
@@ -1931,6 +1939,11 @@ static int vmw_cmd_shader_define(struct vmw_private *dev_priv,
if (unlikely(!dev_priv->has_mob))
return 0;
+ if (unlikely(cmd->header.size < sizeof(cmd->body))) {
+ VMW_DEBUG_USER("Invalid shader define command size.\n");
+ return -EINVAL;
+ }
+
size = cmd->header.size - sizeof(cmd->body);
ret = vmw_compat_shader_add(dev_priv, vmw_context_res_man(ctx),
cmd->body.shid, cmd + 1, cmd->body.type,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 384c6736cf6b..f8cabbe9868a 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -703,6 +703,7 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
if (unlikely(ret != 0)) {
DRM_ERROR("Failed to reference a fence "
"object.\n");
+ ttm_base_object_unref(&base);
goto out_no_ref_obj;
}
handle = base->handle;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
index fd77ab6568cb..f0f2dbb66cdc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_msg.c
@@ -431,7 +431,7 @@ int vmw_host_get_guestinfo(const char *guest_info_param,
/* Remove reply code, which are the first 2 characters of
* the reply
*/
- reply_len = max(reply_len - 2, (size_t) 0);
+ reply_len = reply_len > 2 ? reply_len - 2 : 0;
reply_len = min(reply_len, *length);
if (reply_len > 0)
@@ -572,7 +572,8 @@ int vmw_msg_ioctl(struct drm_device *dev, void *data,
goto out_msg;
}
if (reply && reply_len > 0) {
- if (copy_to_user((void __user *)((unsigned long)arg->receive),
+ if (reply_len > arg->receive_len ||
+ copy_to_user((void __user *)((unsigned long)arg->receive),
reply, reply_len)) {
DRM_ERROR("Failed to copy message to userspace.\n");
kfree(reply);
@@ -1045,7 +1046,7 @@ int vmw_mksstat_add_ioctl(struct drm_device *dev, void *data,
hypervisor_ppn_add((PPN64)page_to_pfn(page));
dev_priv->mksstat_user_pages[slot] = page;
- atomic_set(&dev_priv->mksstat_user_pids[slot], task_pgrp_vnr(current));
+ atomic_set(&dev_priv->mksstat_user_pids[slot], pid_nr(task_pgrp(current)));
arg->id = slot;
@@ -1104,7 +1105,7 @@ int vmw_mksstat_remove_ioctl(struct drm_device *dev, void *data,
DRM_DEV_INFO(dev->dev, "pid=%d arg.id=%zu\n", current->pid, slot);
- pgid = task_pgrp_vnr(current);
+ pgid = pid_nr(task_pgrp(current));
pid = atomic_cmpxchg(&dev_priv->mksstat_user_pids[slot], pgid, MKSSTAT_PID_RESERVED);
if (!pid)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
index bd0563741e89..0104e73b69de 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c
@@ -785,14 +785,26 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
for (j = 0; j < metadata->mip_levels[i]; ++j) {
- uint32_t stride = vmw_surface_calculate_pitch(
- desc, cur_size);
+ uint32_t stride;
+ if (unlikely(cur_size->width == 0 ||
+ cur_size->height == 0 ||
+ cur_size->depth == 0)) {
+ ret = -EINVAL;
+ goto out_no_copy;
+ }
+ stride = vmw_surface_calculate_pitch(desc, cur_size);
cur_offset->face = i;
cur_offset->mip = j;
cur_offset->bo_offset = cur_bo_offset;
- cur_bo_offset += vmw_surface_get_image_buffer_size
- (desc, cur_size, stride);
+ if (unlikely(check_add_overflow(cur_bo_offset,
+ vmw_surface_get_image_buffer_size(desc,
+ cur_size,
+ stride),
+ &cur_bo_offset))) {
+ ret = -EINVAL;
+ goto out_no_copy;
+ }
++cur_offset;
++cur_size;
}
@@ -967,12 +979,14 @@ static int vmw_buffer_prime_to_surface_base(struct vmw_private *dev_priv,
if (ret) {
drm_warn(&dev_priv->drm,
"Couldn't add an object ref for the buffer (%d).\n", *handle);
+ ttm_base_object_unref(&base);
goto out;
}
*base_p = base;
out:
vmw_user_bo_unref(&bo);
+ drm_gem_handle_delete(file_priv, *handle);
return ret;
}
@@ -1440,7 +1454,7 @@ int vmw_gb_surface_reference_ioctl(struct drm_device *dev, void *data,
(union drm_vmw_gb_surface_reference_arg *)data;
struct drm_vmw_surface_arg *req = &arg->req;
struct drm_vmw_gb_surface_ref_rep *rep = &arg->rep;
- struct drm_vmw_gb_surface_ref_ext_rep rep_ext;
+ struct drm_vmw_gb_surface_ref_ext_rep rep_ext = {};
int ret;
ret = vmw_gb_surface_reference_internal(dev, req, &rep_ext, file_priv);
@@ -1720,6 +1734,7 @@ vmw_gb_surface_reference_internal(struct drm_device *dev,
srf = &user_srf->srf;
if (!srf->res.guest_memory_bo) {
DRM_ERROR("Shared GB surface is missing a backup buffer.\n");
+ ret = -EINVAL;
goto out_bad_resource;
}
metadata = &srf->metadata;
@@ -1755,9 +1770,13 @@ vmw_gb_surface_reference_internal(struct drm_device *dev,
SVGA3D_FLAGS_UPPER_32(metadata->flags);
rep->creq.multisample_pattern = metadata->multisample_pattern;
rep->creq.quality_level = metadata->quality_level;
+ rep->creq.buffer_byte_stride = metadata->buffer_byte_stride;
rep->creq.must_be_zero = 0;
out_bad_resource:
+ if (unlikely(ret != 0))
+ ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
+ base->handle);
ttm_base_object_unref(&base);
return ret;
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-09-19 22:34 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-19 22:34 [PATCH] drm/vmwgfx: fix surface size overflow, execbuf bounds, and cursor/msg leaks Hui Peng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®