From: Zack Rusin <zack.rusin@broadcom.com>
To: dri-devel@lists.freedesktop.org
Cc: ian.forbes@broadcom.com, maaz.mombasawala@broadcom.com,
Zack Rusin <zack.rusin@broadcom.com>,
stable@vger.kernel.org
Subject: [PATCH 12/12] drm/vmwgfx: validate external BO copy bounds for both stride paths
Date: Tue, 5 May 2026 18:22:33 -0400 [thread overview]
Message-ID: <20260505222728.519626-13-zack.rusin@broadcom.com> (raw)
In-Reply-To: <20260505222728.519626-1-zack.rusin@broadcom.com>
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 <zack.rusin@broadcom.com>
---
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
prev parent reply other threads:[~2026-05-05 22:28 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20260505222728.519626-1-zack.rusin@broadcom.com>
2026-05-05 22:22 ` [PATCH 01/12] drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size Zack Rusin
2026-05-05 22:22 ` [PATCH 02/12] drm/vmwgfx: reject DX_BIND_QUERY without a DX context Zack Rusin
2026-05-05 22:22 ` [PATCH 03/12] drm/vmwgfx: clamp dirty-page range with min, not max Zack Rusin
2026-05-05 22:22 ` [PATCH 04/12] drm/vmwgfx: take fman->lock around fence list mutation in fifo_down Zack Rusin
2026-05-06 3:59 ` Matthew Brost
2026-05-05 22:22 ` [PATCH 05/12] drm/vmwgfx: drop dma_buf reference on foreign-fd prime import Zack Rusin
2026-05-05 22:22 ` [PATCH 06/12] drm/vmwgfx: validate DRAW_PRIMITIVES header size before division Zack Rusin
2026-05-05 22:22 ` [PATCH 07/12] drm/vmwgfx: bound DMA command body size against suffix pointer Zack Rusin
2026-05-05 22:22 ` [PATCH 08/12] drm/vmwgfx: avoid destroy_workqueue(NULL) on vkms init failure Zack Rusin
2026-05-05 22:22 ` [PATCH 09/12] drm/vmwgfx: enforce cursor size limits for MOB cursors Zack Rusin
2026-05-05 22:22 ` [PATCH 10/12] drm/vmwgfx: skip hash_del_rcu when validation context has no hash table Zack Rusin
2026-05-05 22:22 ` [PATCH 11/12] drm/vmwgfx: use check_add_overflow for shader size+offset bound Zack Rusin
2026-05-05 22:22 ` Zack Rusin [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260505222728.519626-13-zack.rusin@broadcom.com \
--to=zack.rusin@broadcom.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=ian.forbes@broadcom.com \
--cc=maaz.mombasawala@broadcom.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox