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

Reply via email to