vmw_external_bo_copy() trusts caller-supplied offsets, strides, and
heights and operates on imported dma-buf vmaps:

  - The equal-stride memcpy() bound was clamped after subtracting the
    offsets from dst_size and src_size; an offset larger than the BO
    size wraps the unsigned subtraction to a huge value and the
    resulting memcpy() runs off the end of the vmap.  dst_stride *
    height is also a u32 multiplication that can overflow.
  - The non-equal-stride row-by-row path had no bound at all.  The
    loop touches bytes through offset + (height - 1) * stride +
    width_in_bytes, with only a WARN_ON(dst_stride < width_in_bytes),
    and could likewise step past the end of either mapping.

The offsets and strides are derived from STDU/SOU plane state, so a
configured CRTC submitting a crafted atomic commit on an imported
framebuffer can reach this path.

Validate the exact row-copy endpoint against each BO's size up front
using check_mul_overflow() and check_add_overflow().  Use the bulk
memcpy() path only when width_in_bytes covers the whole stride;
otherwise copy one row at a time so partial-row updates near the bottom
of a framebuffer remain valid.  Also reject zero strides and stride <
width_in_bytes, both of which the row-by-row path cannot represent
safely.

Fixes: 50f119925091 ("drm/vmwgfx: Fix prime with external buffers")
Cc: [email protected]
Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Zack Rusin <[email protected]>
---
 drivers/gpu/drm/vmwgfx/vmwgfx_blit.c | 39 ++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c 
b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
index 135b75a3e013..56f965ec99dc 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
@@ -30,6 +30,7 @@
 
 #include "vmwgfx_bo.h"
 #include <linux/highmem.h>
+#include <linux/overflow.h>
 
 /*
  * Template that implements find_first_diff() for a generic
@@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 
dst_offset,
                container_of(dst->tbo.bdev, struct vmw_private, bdev);
        size_t dst_size = dst->tbo.resource->size;
        size_t src_size = src->tbo.resource->size;
+       size_t dst_end, src_end;
        struct iosys_map dst_map = {0};
        struct iosys_map src_map = {0};
+       bool dst_mapped = false;
+       bool src_mapped = false;
        int ret, i;
        int x_in_bytes;
        u8 *vsrc;
        u8 *vdst;
 
+       if (!height || !width_in_bytes)
+               return 0;
+
+       if (!dst_stride || !src_stride)
+               return -EINVAL;
+       if (dst_stride < width_in_bytes || src_stride < width_in_bytes)
+               return -EINVAL;
+       if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, 
&dst_end) ||
+           check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) ||
+           check_add_overflow((size_t)dst_offset, dst_end, &dst_end) ||
+           dst_end > dst_size ||
+           check_mul_overflow((size_t)src_stride, (size_t)height - 1, 
&src_end) ||
+           check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) ||
+           check_add_overflow((size_t)src_offset, src_end, &src_end) ||
+           src_end > src_size) {
+               drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n");
+               return -EINVAL;
+       }
+
        vsrc = map_external(src, &src_map);
        if (!vsrc) {
                drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n");
                ret = -ENOMEM;
                goto out;
        }
+       src_mapped = true;
 
        vdst = map_external(dst, &dst_map);
        if (!vdst) {
@@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 
dst_offset,
                ret = -ENOMEM;
                goto out;
        }
+       dst_mapped = true;
 
        vsrc += src_offset;
        vdst += dst_offset;
-       if (src_stride == dst_stride) {
-               dst_size -= dst_offset;
-               src_size -= src_offset;
-               memcpy(vdst, vsrc,
-                      min(dst_stride * height, min(dst_size, src_size)));
+       if (src_stride == dst_stride && width_in_bytes == dst_stride) {
+               memcpy(vdst, vsrc, dst_stride * (size_t)height);
        } else {
-               WARN_ON(dst_stride < width_in_bytes);
                for (i = 0; i < height; ++i) {
                        memcpy(vdst, vsrc, width_in_bytes);
                        vsrc += src_stride;
@@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 
dst_offset,
 
        ret = 0;
 out:
-       unmap_external(src, &src_map);
-       unmap_external(dst, &dst_map);
+       if (src_mapped)
+               unmap_external(src, &src_map);
+       if (dst_mapped)
+               unmap_external(dst, &dst_map);
 
        return ret;
 }
-- 
2.51.0

Reply via email to