vmw_cmd_dma() locates the SVGA3dCmdSurfaceDMASuffix at the end of the
command and dereferences it:

        suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body +
                                               header->size - sizeof(*suffix));
        if (unlikely(suffix->suffixSize != sizeof(*suffix))) {

header->size comes straight from userspace through DRM_IOCTL_VMW_EXECBUF
(DRM_RENDER_ALLOW) and is only bounded from above: vmw_cmd_check()
rejects header->size > SVGA_CMD_MAX_DATASIZE and commands that do not fit
the buffer, and struct vmw_cmd_entry carries no per-command minimum size.
There is no lower bound. When header->size is smaller than sizeof(*suffix)
the pointer arithmetic underflows and the read of suffix->suffixSize is an
out-of-bounds read ahead of the command; when it is smaller than the body
plus the suffix, the body accesses that follow are out of bounds as well.

Reject the command before computing the suffix pointer when header->size
cannot hold both the fixed body and the trailing suffix.

Found by a syzkaller instance fuzzing the vmwgfx command stream:

  BUG: KASAN: vmalloc-out-of-bounds in vmw_cmd_dma+0x508/0x5b0
  Read of size 4 ...
   vmw_cmd_dma+0x508/0x5b0 drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
   vmw_execbuf_process+0xd06/0x35f0
   vmw_execbuf_ioctl+0x1cc/0x5a0

Fixes: cbd75e97a525 ("drm/vmwgfx: Make sure user-space can't DMA across buffer 
object boundaries v2")
Cc: [email protected]
Signed-off-by: Aldo Ariel Panzardo <[email protected]>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 6b921db2dcd2..02eb383f91b5 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -1524,6 +1524,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv,
        bool dirty;
 
        cmd = container_of(header, typeof(*cmd), header);
+
+       if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) {
+               VMW_DEBUG_USER("Invalid SURFACE_DMA command size.\n");
+               return -EINVAL;
+       }
+
        suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body +
                                               header->size - sizeof(*suffix));
 
-- 
2.43.0

Reply via email to