[PATCH 3/7] drm/msm: check UBWC framebuffers against their layout
From: Dmitry Baryshkov
Date: Wed Nov 04 2026 - 12:12:03 EST
msm_framebuffer_init() checks every framebuffer plane as a linear one:
(height - 1) * pitch + width * cpp + offset must fit into the buffer.
For UBWC this has nothing to do with the memory the DPU fetches. The
metadata and the scanline padding are not accounted for, so a 1080p NV12
framebuffer whose buffer is 15872 bytes short of the UBWC layout is
accepted and the DPU reads past its end. The CbCr planes are checked
where the DPU does not look: it always fetches them after the Y data in
the first buffer, ignoring handles[1], offsets[1] and pitches[1], and
userspace relies on that (turnip reports offset 0 for both planes). The
pitches are not checked at all, as the DPU replaces them with its own.
Compute the UBWC layout instead: require the first pitch to match it,
the CbCr plane to be in the first buffer, and check that the metadata
and data of all planes fit into it.
Fixes: 25fdd5933e4c ("drm/msm: Add SDM845 DPU support")
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxxxxxxxx>
---
drivers/gpu/drm/msm/msm_fb.c | 39 ++++++++++++++++++++++++++++++++++-----
1 file changed, 34 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c
index 1e236416e15f..dd24aa7a5ee8 100644
--- a/drivers/gpu/drm/msm/msm_fb.c
+++ b/drivers/gpu/drm/msm/msm_fb.c
@@ -13,6 +13,7 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_probe_helper.h>
+#include "disp/mdp_format.h"
#include "msm_drv.h"
#include "msm_kms.h"
#include "msm_gem.h"
@@ -146,6 +147,7 @@ msm_framebuffer_init(struct drm_device *dev, const struct drm_format_info *info,
struct msm_framebuffer *msm_fb = NULL;
struct drm_framebuffer *fb;
const struct msm_format *format;
+ u32 ubwc_pitch[4], ubwc_size[4];
int ret, i, n;
drm_dbg_state(dev, "create framebuffer: mode_cmd=%p (%dx%d@%p4cc)\n",
@@ -177,14 +179,41 @@ msm_framebuffer_init(struct drm_device *dev, const struct drm_format_info *info,
goto fail;
}
+ if (format->fetch_mode == MDP_FETCH_UBWC)
+ mdp_format_ubwc_plane_sizes(format, mode_cmd->width,
+ mode_cmd->height, ubwc_pitch,
+ ubwc_size);
+
for (i = 0; i < n; i++) {
unsigned int width = mode_cmd->width / (i ? info->hsub : 1);
unsigned int height = mode_cmd->height / (i ? info->vsub : 1);
- unsigned int min_size;
-
- min_size = (height - 1) * mode_cmd->pitches[i]
- + width * info->cpp[i]
- + mode_cmd->offsets[i];
+ u64 min_size;
+
+ if (format->fetch_mode == MDP_FETCH_UBWC) {
+ /*
+ * The CbCr planes follow the Y data in the first buffer,
+ * offsets[1] and pitches[1] are not used.
+ */
+ if (i > 0) {
+ if (bos[i] != bos[0]) {
+ ret = UERR(EINVAL, dev,
+ "plane %d not in the first buffer", i);
+ goto fail;
+ }
+ min_size = 0;
+ } else if (mode_cmd->pitches[0] != ubwc_pitch[0]) {
+ ret = UERR(EINVAL, dev, "pitch %u, expected %u",
+ mode_cmd->pitches[0], ubwc_pitch[0]);
+ goto fail;
+ } else {
+ min_size = (u64)mode_cmd->offsets[0] + ubwc_size[0] +
+ ubwc_size[1] + ubwc_size[2] + ubwc_size[3];
+ }
+ } else {
+ min_size = (height - 1) * mode_cmd->pitches[i]
+ + width * info->cpp[i]
+ + mode_cmd->offsets[i];
+ }
if (bos[i]->size < min_size) {
ret = UERR(EINVAL, dev, "plane %d too small", i);
--
2.47.3