drm/vmwgfx: enforce cursor size limits for MOB cursors

vmw_cursor_plane_atomic_check() bounds cursor width and height only
on the legacy update path; the SVGA_CAP2_CURSOR_MOB path -- the
default on modern hosts -- accepts any size.  When the requested size
exceeds SVGA_REG_CURSOR_MAX_DIMENSION or SVGA_REG_MOB_MAX_SIZE,
vmw_cursor_mob_get() returns -EINVAL and leaves vps->cursor.mob NULL.
Its return value is then discarded in vmw_cursor_plane_prepare_fb(),
so the subsequent vmw_cursor_update_mob() calls
vmw_bo_map_and_cache(NULL) and oopses inside
vmw_bo_map_and_cache_size() on the tbo.base.size load.

Reachable from any DRM master via DRM_IOCTL_MODE_CURSOR2 with a
sufficiently large width or height (e.g. cursor_max_dim + 1).

Reject oversized cursors in atomic_check for both MOB-backed cursor
update types.  The MOB byte-size limit only applies to the
SVGA_CAP2_CURSOR_MOB path (vmw_cursor_mob_size() returns 0 for
GB_ONLY); compute the required MOB size in 64-bit to avoid overflow
when very large dimensions are requested.

In prepare_fb only call vmw_cursor_mob_get()/_map() for
VMW_CURSOR_UPDATE_MOB -- the GB_ONLY path uses bo->map.virtual
directly and would otherwise be silently downgraded to NONE on hosts
without SVGA_CAP2_CURSOR_MOB (where vmw_cursor_mob_get() always
returns -EINVAL).  Degrade the update to NONE if vmw_cursor_mob_get()
or vmw_cursor_mob_map() fails so the update path does not run with a
NULL backing MOB.

Fixes: 965544150d ("drm/vmwgfx: Refactor cursor handling")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
Link: https://patch.msgid.link/20260505222728.519626-10-zack.rusin@broadcom.com
This commit is contained in:
Zack Rusin 2026-05-05 18:22:30 -04:00
parent 05eaa887e7
commit d5ed874916

View File

@ -432,6 +432,7 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps)
u32 size = vmw_cursor_mob_size(vps->cursor.update_type,
vps->base.crtc_w, vps->base.crtc_h);
struct vmw_bo *vbo = vps->cursor.mob;
void *map;
if (!vbo)
return -EINVAL;
@ -446,11 +447,15 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps)
if (unlikely(ret != 0))
return -ENOMEM;
vmw_bo_map_and_cache(vbo);
map = vmw_bo_map_and_cache(vbo);
if (!map) {
vmw_bo_unmap(vbo);
ret = -ENOMEM;
}
ttm_bo_unreserve(&vbo->tbo);
return 0;
return ret;
}
/**
@ -663,9 +668,15 @@ int vmw_cursor_plane_prepare_fb(struct drm_plane *plane,
!vmw_cursor_buffer_changed(vps, old_vps)) {
vps->cursor.update_type =
VMW_CURSOR_UPDATE_NONE;
} else {
vmw_cursor_mob_get(vcp, vps);
vmw_cursor_mob_map(vps);
} else if (vps->cursor.update_type ==
VMW_CURSOR_UPDATE_MOB &&
(vmw_cursor_mob_get(vcp, vps) ||
vmw_cursor_mob_map(vps))) {
/*
* Reset the cursor to avoid crashes later.
*/
vps->cursor.update_type =
VMW_CURSOR_UPDATE_NONE;
}
}
}
@ -732,6 +743,34 @@ int vmw_cursor_plane_atomic_check(struct drm_plane *plane,
"surface not suitable for cursor\n");
return -EINVAL;
}
} else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY ||
update_type == VMW_CURSOR_UPDATE_MOB) {
u32 cursor_max_dim =
vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION);
if (new_state->crtc_w > cursor_max_dim ||
new_state->crtc_h > cursor_max_dim) {
drm_warn(&vmw->drm,
"Cursor dimensions (%d, %d) exceed device max %u\n",
new_state->crtc_w, new_state->crtc_h,
cursor_max_dim);
return -EINVAL;
}
if (update_type == VMW_CURSOR_UPDATE_MOB) {
u32 mob_max_size =
vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE);
u64 mob_size = (u64)new_state->crtc_w *
new_state->crtc_h * sizeof(u32) +
sizeof(SVGAGBCursorHeader);
if (mob_size > mob_max_size) {
drm_warn(&vmw->drm,
"Cursor MOB size %llu exceeds device max %u\n",
mob_size, mob_max_size);
return -EINVAL;
}
}
}
return 0;