vmw_cmd_res_switch_backup lets userspace bind an arbitrary MOB to a
resource without checking that the MOB is large enough for the resource's
guest_memory_size. The dirty-tracking bitmap is allocated based on the
MOB size, but later accessed using the (larger) surface size, causing an
out-of-bounds write in the bitmap that corrupts adjacent slab memory.
Add a check that backup_offset + res->guest_memory_size does not exceed
the buffer size before allowing the backup switch.
DX shaders and stream outputs never set guest_memory_size, and both were
allocated with kmalloc(), so the check would read uninitialized memory for
SVGA_3D_CMD_DX_BIND_SHADER and SVGA_3D_CMD_DX_BIND_STREAMOUTPUT and could
reject valid binds. Allocate both with kzalloc() so their
guest_memory_size is 0 and the check only bounds the offset.
Fixes: a97e21923b42 ("drm/vmwgfx: Hook up guest-backed surfaces")
Cc: [email protected]
Reported-by: Zero Day Initiative <[email protected]>
Reported-by: [email protected] # ZDI-CAN-34068
Closes:
https://lore.kernel.org/all/byapr01mb38301cfc012e873340f77e3d80...@byapr01mb3830.prod.exchangelabs.com/
Signed-off-by: Zack Rusin <[email protected]>
---
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 ++++++++
drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 2 +-
drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c | 2 +-
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index a9136a6523cb..0156aee84c9f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -1718,6 +1718,7 @@ static int vmw_cmd_res_switch_backup(struct vmw_private
*dev_priv,
{
struct vmw_bo *vbo;
void *info;
+ u64 end;
int ret;
info = vmw_execbuf_info_from_res(sw_context, res);
@@ -1728,6 +1729,13 @@ static int vmw_cmd_res_switch_backup(struct vmw_private
*dev_priv,
if (ret)
return ret;
+ if (check_add_overflow((u64)backup_offset,
+ (u64)res->guest_memory_size, &end) ||
+ end > vbo->tbo.base.size) {
+ VMW_DEBUG_USER("Backup buffer too small for resource.\n");
+ return -EINVAL;
+ }
+
vmw_validation_res_switch_backup(sw_context->ctx, info, vbo,
backup_offset);
return 0;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
index 39811cf19db1..033157efb1fc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c
@@ -597,7 +597,7 @@ int vmw_dx_shader_add(struct vmw_cmdbuf_res_manager *man,
if (!vmw_shader_id_ok(user_key, shader_type))
return -EINVAL;
- shader = kmalloc_obj(*shader);
+ shader = kzalloc_obj(*shader);
if (!shader) {
return -ENOMEM;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c
b/drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c
index 52c6cfa163da..34d5b71fa42f 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_streamoutput.c
@@ -284,7 +284,7 @@ int vmw_dx_streamoutput_add(struct vmw_cmdbuf_res_manager
*man,
struct vmw_private *dev_priv = ctx->dev_priv;
int ret;
- so = kmalloc_obj(*so);
+ so = kzalloc_obj(*so);
if (!so) {
return -ENOMEM;
}
base-commit: 8ef66514d7b993c9fb96a5886f001e3556610c1c
--
2.53.0