Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/vc4: hvs: Fix buffer overflow with the dlist handling

Commit 0a038c1c29a7 ("drm/vc4: Move LBM creation out of
vc4_plane_mode_set()") changed the LBM allocation logic from first
allocating the LBM memory for the plane to running mode_set,
adding a gap in the LBM, and then running the dlist allocation filling
that gap.

The gap was introduced by incrementing the dlist array index, but was
never checking whether or not we were over the array length, leading
eventually to memory corruptions if we ever crossed this limit.

vc4_dlist_write had that logic though, and was reallocating a larger
dlist array when reaching the end of the buffer. Let's share the logic
between both functions.

Cc: Boris Brezillon <boris.brezillon@collabora.com>
Cc: Eric Anholt <eric@anholt.net>
Fixes: 0a038c1c29a7 ("drm/vc4: Move LBM creation out of vc4_plane_mode_set()")
Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20210129160647.128373-1-maxime@cerno.tech

+14 -4
+14 -4
drivers/gpu/drm/vc4/vc4_plane.c
··· 220 220 __drm_atomic_helper_plane_reset(plane, &vc4_state->base); 221 221 } 222 222 223 - static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) 223 + static void vc4_dlist_counter_increment(struct vc4_plane_state *vc4_state) 224 224 { 225 225 if (vc4_state->dlist_count == vc4_state->dlist_size) { 226 226 u32 new_size = max(4u, vc4_state->dlist_count * 2); ··· 235 235 vc4_state->dlist_size = new_size; 236 236 } 237 237 238 - vc4_state->dlist[vc4_state->dlist_count++] = val; 238 + vc4_state->dlist_count++; 239 + } 240 + 241 + static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val) 242 + { 243 + unsigned int idx = vc4_state->dlist_count; 244 + 245 + vc4_dlist_counter_increment(vc4_state); 246 + vc4_state->dlist[idx] = val; 239 247 } 240 248 241 249 /* Returns the scl0/scl1 field based on whether the dimensions need to ··· 986 978 * be set when calling vc4_plane_allocate_lbm(). 987 979 */ 988 980 if (vc4_state->y_scaling[0] != VC4_SCALING_NONE || 989 - vc4_state->y_scaling[1] != VC4_SCALING_NONE) 990 - vc4_state->lbm_offset = vc4_state->dlist_count++; 981 + vc4_state->y_scaling[1] != VC4_SCALING_NONE) { 982 + vc4_state->lbm_offset = vc4_state->dlist_count; 983 + vc4_dlist_counter_increment(vc4_state); 984 + } 991 985 992 986 if (num_planes > 1) { 993 987 /* Emit Cb/Cr as channel 0 and Y as channel