From: Zack Rusin Date: Tue, 5 May 2026 22:22:33 +0000 (-0400) Subject: drm/vmwgfx: validate external BO copy bounds for both stride paths X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=706c93c5813caabbb0d0a576c017d15aeec2c113;p=thirdparty%2Flinux.git drm/vmwgfx: validate external BO copy bounds for both stride paths 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: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-13-zack.rusin@broadcom.com --- 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 +#include /* * 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; }