mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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: 50f1199250 ("drm/vmwgfx: Fix prime with external buffers")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Zack Rusin <zack.rusin@broadcom.com>
Reviewed-by: Ian Forbes <ian.forbes@broadcom.com>
Link: https://patch.msgid.link/20260505222728.519626-13-zack.rusin@broadcom.com
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user