From 2deb0b6c0ed26551aed332b8664a2c640cb9839e Mon Sep 17 00:00:00 2001 From: Jonathan Frazin Date: Wed, 23 Sep 2026 15:14:56 -0500 Subject: [PATCH 1/2] drm/mipi-dbi: clamp the flushed rectangle to the panel's fixed mode mipi_dbi_fb_dirty() addresses the panel controller using the plane source rectangle origin subtracted from the damage rectangle (added in this branch's earlier honour-the-plane-source-offset change, PR #7589). That subtraction assumed the result would already be panel-sized. The damage rectangle from drm_atomic_helper_damage_merged() is clipped against the plane source rectangle's exact 16.16 fixed-point bounds, while the origin subtracted here is that same value truncated to whole pixels. When the origin has a fractional part, the truncation can leave the rectangle's far edge up to a pixel past where a whole-pixel origin would place the panel's own width/height - and tx_buf is sized for exactly the panel, with no slack for that overshoot: a heap buffer overflow. Clamp the rectangle to the panel's fixed mode before using it for the window address or the transfer length. A damage clip can also lie entirely within that one-pixel sliver past the panel edge - the damage iterator clips only against the exact fixed-point bound, not the whole-pixel one used here - collapsing the clamped rectangle to zero width or height. Skip the flush in that case rather than program an inverted (start past end) address window, which is undefined behaviour per the MIPI DCS spec. Found by automated review on the equivalent upstream submission (drm/mipi-dbi: display a cropped region of an oversized framebuffer, dri-devel), across two review rounds; folding both fixes back here since this code has been merged and in use on rpi-7.2.y since PR #7589, ahead of upstream landing it. Signed-off-by: Jonathan Frazin --- drivers/gpu/drm/drm_mipi_dbi.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c index 38db45a9dbc35e..bb557fab1eb039 100644 --- a/drivers/gpu/drm/drm_mipi_dbi.c +++ b/drivers/gpu/drm/drm_mipi_dbi.c @@ -275,16 +275,41 @@ static void mipi_dbi_fb_dirty(struct iosys_map *src, struct drm_framebuffer *fb, struct drm_format_conv_state *fmtcnv_state) { struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev); - unsigned int height = rect->y2 - rect->y1; - unsigned int width = rect->x2 - rect->x1; const struct drm_format_info *dst_format; struct mipi_dbi *dbi = &dbidev->dbi; bool swap = dbi->swap_bytes; + unsigned int height, width; int ret = 0; size_t len; bool full; void *tr; + /* + * @rect is in framebuffer coordinates, clipped to the plane's src + * rectangle by the damage iterator against that rectangle's exact + * 16.16 fixed-point bounds. @src_x/@src_y are that same origin + * truncated to whole pixels. When the origin has a fractional part, + * that truncation can leave @rect's far edge up to a pixel past + * where a whole-pixel @src_x/@src_y would place the panel's own + * width/height -- and tx_buf is sized for exactly the panel, with no + * slack for that overshoot. Clamp before using @rect for anything. + * + * A damage clip can lie entirely in that one-pixel sliver past the + * panel edge -- the iterator above clips only against the exact + * fixed-point bound, not the whole-pixel one used here -- in which + * case the clamp collapses @rect to zero width or height. Nothing in + * it was ever visible on the panel, so skip the flush rather than + * program an inverted (start past end) address window. + */ + rect->x2 = min_t(int, rect->x2, src_x + dbidev->mode.hdisplay); + rect->y2 = min_t(int, rect->y2, src_y + dbidev->mode.vdisplay); + + if (rect->x2 <= rect->x1 || rect->y2 <= rect->y1) + return; + + height = rect->y2 - rect->y1; + width = rect->x2 - rect->x1; + full = width == fb->width && height == fb->height; DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); From 3aafcd93cbb9c4a5f0a72c3808fe99f006d3c792 Mon Sep 17 00:00:00 2001 From: Jonathan Frazin Date: Wed, 23 Sep 2026 15:50:30 -0500 Subject: [PATCH 2/2] drm/sitronix: allow a framebuffer larger than the panel on st7735r st7735r flushes through the shared drm_mipi_dbi_plane_helper_atomic_update(), same as the six drm/tiny drivers PR #7589 already raised mode_config.max_width/max_height on - but it lives under drivers/gpu/drm/sitronix/ rather than drivers/gpu/drm/tiny/ and was missed from that original sweep. Raise its limits the same way, to DRM_SHADOW_PLANE_MAX_WIDTH/HEIGHT. Confirmed by grepping the whole tree for DRM_MIPI_DBI_PLANE_HELPER_FUNCS that no other driver shares this flush path and is still missing this. Found by automated review on the equivalent upstream submission (drm/tiny: allow a framebuffer larger than the panel on MIPI DBI drivers, dri-devel, v3 about to be sent); folding the same fix back here for the same reason as the sibling commit on this branch. Signed-off-by: Jonathan Frazin --- drivers/gpu/drm/sitronix/st7735r.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/sitronix/st7735r.c b/drivers/gpu/drm/sitronix/st7735r.c index bd763871dca0c8..e78bd2da3e46c4 100644 --- a/drivers/gpu/drm/sitronix/st7735r.c +++ b/drivers/gpu/drm/sitronix/st7735r.c @@ -295,9 +295,13 @@ static int st7735r_probe(struct spi_device *spi) return ret; drm->mode_config.min_width = dbidev->mode.hdisplay; - drm->mode_config.max_width = dbidev->mode.hdisplay; + /* + * Allow a framebuffer larger than the panel so a sub-region can be + * displayed via the plane source rectangle (crop / pan, no scaling). + */ + drm->mode_config.max_width = DRM_SHADOW_PLANE_MAX_WIDTH; drm->mode_config.min_height = dbidev->mode.vdisplay; - drm->mode_config.max_height = dbidev->mode.vdisplay; + drm->mode_config.max_height = DRM_SHADOW_PLANE_MAX_HEIGHT; drm->mode_config.funcs = &st7735r_mode_config_funcs; drm->mode_config.preferred_depth = 16; drm->mode_config.helper_private = &st7735r_mode_config_helper_funcs;