Linux Media Controller development
 help / color / mirror / Atom feed
* [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts
@ 2026-06-17  2:18 Michael Bommarito
  2026-06-17  2:18 ` [PATCH v3 1/9] media: v4l2-ctrls: validate HEVC " Michael Bommarito
                   ` (9 more replies)
  0 siblings, 10 replies; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:18 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

The stateless HEVC and AV1 controls carry tile counts that several SoC
decoder drivers consume as loop bounds and array indices when laying out
fixed-size hardware descriptor buffers. std_validate_compound() does not
bound them, so a crafted HEVC PPS or AV1 frame control can drive
out-of-bounds writes and an AV1 divide-by-zero in the rkvdec, hantro,
rockchip and mediatek decoders.

  1-2  reject out-of-range HEVC and AV1 tile counts in
       std_validate_compound() (one patch per codec). For AV1 the per-
       dimension bound is V4L2_AV1_MAX_TILE_{COLS,ROWS} and the total is
       V4L2_AV1_MAX_TILE_COUNT.
  3    add <media/v4l2-hevc.h> with bounded tile-count helpers.
  4-5  use the helpers in rkvdec and hantro instead of open-coding the
       clamp; rkvdec also bails before indexing the hardware
       parameter-set table with an out-of-range HEVC PPS id.
  6    guard the rockchip VPU981 AV1 divisor against tile_cols == 0 and
       keep the descriptor writes inside the AV1_MAX_TILES buffer.
  7    reject a rockchip AV1 frame whose tile_cols * tile_rows exceeds the
       submitted tile group entry count or the AV1_MAX_TILES descriptor
       capacity, which set_tile_info() would otherwise read past or leave
       under-described while programming the larger geometry.
  8    bound the mediatek AV1 tile-start copy.
  9    KUnit coverage for the tile-count validation.

Changes since v2:
  - Split the combined HEVC+AV1 validation into one patch per codec, each
    with a single Fixes tag (Benjamin Gaignard).
  - Move the AV1 total-tile bound into validate_av1_tile_info() using the
    uAPI V4L2_AV1_MAX_TILE_COUNT, instead of clamping tile_cols/tile_rows
    in the rockchip driver, which would have corrupted the values written
    to the hardware registers (Benjamin Gaignard's NACK on v2 4/6).
  - Add <media/v4l2-hevc.h> with shared bounded tile-count helpers so
    rkvdec and hantro no longer duplicate the clamp (Benjamin Gaignard).
  - New patch 7: reject a rockchip AV1 frame that claims more tiles than
    the submitted tile group entry array holds (set_tile_info() indexes
    it by tile_cols * tile_rows) or more than AV1_MAX_TILES (the hardware
    descriptor buffer), which would otherwise leave the hardware
    programmed for more tiles than the buffer describes. mediatek already
    guards the entry count; rockchip now guards both.

checkpatch --strict: 0 errors on all nine. Patches 3 and 9 each carry one
"added file ... does MAINTAINERS need updating?" warning for the new
<media/v4l2-hevc.h> and the KUnit test file; both already fall under the
existing include/media/ and drivers/media/v4l2-core/ MAINTAINERS entries,
so no MAINTAINERS change is needed. (checkpatch's SPDX sub-check did not
run in my environment -- spdxcheck.py needs python3-ply -- but the SPDX
headers are present on both new files.)

The tile-count validation is exercised with KUnit (patch 9): in-range
HEVC/AV1 counts pass, out-of-range per-dimension counts and an AV1 grid
whose product exceeds V4L2_AV1_MAX_TILE_COUNT are rejected, and the
zero-initialised AV1 frame control that v4l2-compliance and existing
userspace submit still passes.

v2: https://lore.kernel.org/all/20260614155609.3107600-1-michael.bommarito@gmail.com/

Michael Bommarito (9):
  media: v4l2-ctrls: validate HEVC tile counts
  media: v4l2-ctrls: validate AV1 tile counts
  media: hevc: add bounded tile-count helpers
  media: rkvdec: bound HEVC tile loops and PPS id to the array capacity
  media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer
    capacity
  media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer
  media: verisilicon: rockchip: reject AV1 frames exceeding the tile
    capacity
  media: mediatek: vcodec: bound AV1 tile-start copy to the array
    capacity
  media: v4l2-ctrls: add KUnit tests for compound control tile
    validation

 .../vcodec/decoder/vdec/vdec_av1_req_lat_if.c |   5 +-
 .../rockchip/rkvdec/rkvdec-hevc-common.c      |  14 +-
 .../platform/rockchip/rkvdec/rkvdec-hevc.c    |   7 +-
 .../rockchip/rkvdec/rkvdec-vdpu381-hevc.c     |   2 +
 .../platform/verisilicon/hantro_g2_hevc_dec.c |   6 +-
 .../verisilicon/rockchip_vpu981_hw_av1_dec.c  |  57 +++++--
 drivers/media/v4l2-core/Kconfig               |  12 ++
 .../media/v4l2-core/v4l2-ctrls-core-test.c    | 145 ++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ctrls-core.c     |  36 +++++
 include/media/v4l2-hevc.h                     |  41 +++++
 10 files changed, 306 insertions(+), 19 deletions(-)
 create mode 100644 drivers/media/v4l2-core/v4l2-ctrls-core-test.c
 create mode 100644 include/media/v4l2-hevc.h


base-commit: e24a98d6884a6e4203a77a94f070a59fcab95208
-- 
2.53.0


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v3 1/9] media: v4l2-ctrls: validate HEVC tile counts
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
@ 2026-06-17  2:18 ` Michael Bommarito
  2026-09-03  6:50   ` Benjamin Gaignard
  2026-06-17  2:18 ` [PATCH v3 2/9] media: v4l2-ctrls: validate AV1 " Michael Bommarito
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:18 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

The stateless HEVC decoders read num_tile_columns_minus1 + 1 entries from
column_width_minus1[] and num_tile_rows_minus1 + 1 from row_height_minus1[]
and use them as tile-loop bounds, but std_validate_compound() does not
bound these u8 counts. Reject a V4L2_CTRL_TYPE_HEVC_PPS with tiling
enabled whose tile counts exceed the uAPI array capacity, mirroring the
existing compound-control range checks.

Fixes: 256fa3920874 ("media: v4l: Add definitions for HEVC stateless decoding")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 drivers/media/v4l2-core/v4l2-ctrls-core.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 6b375720e395c..6d478e1a5ef22 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -1242,6 +1242,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
 
 			p_hevc_pps->flags &=
 				~V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED;
+		} else {
+			/*
+			 * These count the entries the stateless HEVC drivers
+			 * read from column_width_minus1[] / row_height_minus1[]
+			 * and use as tile-loop bounds.
+			 */
+			if (p_hevc_pps->num_tile_columns_minus1 >=
+			    ARRAY_SIZE(p_hevc_pps->column_width_minus1))
+				return -EINVAL;
+			if (p_hevc_pps->num_tile_rows_minus1 >=
+			    ARRAY_SIZE(p_hevc_pps->row_height_minus1))
+				return -EINVAL;
 		}
 
 		if (p_hevc_pps->flags &
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 2/9] media: v4l2-ctrls: validate AV1 tile counts
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
  2026-06-17  2:18 ` [PATCH v3 1/9] media: v4l2-ctrls: validate HEVC " Michael Bommarito
@ 2026-06-17  2:18 ` Michael Bommarito
  2026-09-03  6:50   ` Benjamin Gaignard
  2026-06-17  2:19 ` [PATCH v3 3/9] media: hevc: add bounded tile-count helpers Michael Bommarito
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:18 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

The stateless AV1 decoders use tile_info.tile_cols and tile_rows as loop
bounds and as indices into the mi_*_starts[] and *_in_sbs_minus_1[]
arrays, as the divisor for context_update_tile_id, and their product
bounds the per-tile descriptor buffers, but std_validate_compound() does
not bound these u8 fields. Reject a V4L2_CTRL_TYPE_AV1_FRAME whose
tile_cols or tile_rows exceeds V4L2_AV1_MAX_TILE_COLS / _ROWS, or whose
product exceeds V4L2_AV1_MAX_TILE_COUNT. A zero tile count is left to the
consuming driver so the zero-initialised control that existing userspace
submits is still accepted.

Fixes: 9de30f579980 ("media: Add AV1 uAPI")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 drivers/media/v4l2-core/v4l2-ctrls-core.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index 6d478e1a5ef22..fb20ad13dfec7 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -790,10 +790,30 @@ static int validate_av1_film_grain(struct v4l2_ctrl_av1_film_grain *fg)
 	return 0;
 }
 
+static int validate_av1_tile_info(struct v4l2_av1_tile_info *t)
+{
+	/*
+	 * tile_cols and tile_rows index the per-tile descriptor arrays and
+	 * bound the tile loops in the stateless AV1 drivers; the product
+	 * bounds the total tile descriptor count.
+	 */
+	if (t->tile_cols > V4L2_AV1_MAX_TILE_COLS ||
+	    t->tile_rows > V4L2_AV1_MAX_TILE_ROWS)
+		return -EINVAL;
+
+	if ((u32)t->tile_cols * t->tile_rows > V4L2_AV1_MAX_TILE_COUNT)
+		return -EINVAL;
+
+	return 0;
+}
+
 static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f)
 {
 	int ret = 0;
 
+	ret = validate_av1_tile_info(&f->tile_info);
+	if (ret)
+		return ret;
 	ret = validate_av1_quantization(&f->quantization);
 	if (ret)
 		return ret;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 3/9] media: hevc: add bounded tile-count helpers
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
  2026-06-17  2:18 ` [PATCH v3 1/9] media: v4l2-ctrls: validate HEVC " Michael Bommarito
  2026-06-17  2:18 ` [PATCH v3 2/9] media: v4l2-ctrls: validate AV1 " Michael Bommarito
@ 2026-06-17  2:19 ` Michael Bommarito
  2026-09-03  6:51   ` Benjamin Gaignard
  2026-06-17  2:19 ` [PATCH v3 4/9] media: rkvdec: bound HEVC tile loops and PPS id to the array capacity Michael Bommarito
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:19 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

The stateless HEVC decoders compute the number of tile columns and rows
from num_tile_columns_minus1 / num_tile_rows_minus1 and clamp it to the
column_width_minus1[] / row_height_minus1[] capacity before using it as a
loop bound. Add shared helpers in a new <media/v4l2-hevc.h> so the rkvdec
and hantro drivers do not each open-code the min_t() clamp.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 include/media/v4l2-hevc.h | 41 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 include/media/v4l2-hevc.h

diff --git a/include/media/v4l2-hevc.h b/include/media/v4l2-hevc.h
new file mode 100644
index 0000000000000..973c96be16be4
--- /dev/null
+++ b/include/media/v4l2-hevc.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Helper functions for HEVC stateless codecs.
+ */
+
+#ifndef _MEDIA_V4L2_HEVC_H
+#define _MEDIA_V4L2_HEVC_H
+
+#include <linux/minmax.h>
+#include <media/v4l2-ctrls.h>
+
+/**
+ * v4l2_hevc_pps_num_tile_columns - number of HEVC tile columns, bounded
+ * @pps: the V4L2 HEVC PPS control
+ *
+ * Return the number of tile columns (num_tile_columns_minus1 + 1) clamped to
+ * the capacity of column_width_minus1[]. The control validation already
+ * rejects out-of-range counts; this keeps the consuming drivers bounded too.
+ */
+static inline unsigned int
+v4l2_hevc_pps_num_tile_columns(const struct v4l2_ctrl_hevc_pps *pps)
+{
+	return min_t(unsigned int, pps->num_tile_columns_minus1 + 1,
+		     ARRAY_SIZE(pps->column_width_minus1));
+}
+
+/**
+ * v4l2_hevc_pps_num_tile_rows - number of HEVC tile rows, bounded
+ * @pps: the V4L2 HEVC PPS control
+ *
+ * Return the number of tile rows (num_tile_rows_minus1 + 1) clamped to the
+ * capacity of row_height_minus1[].
+ */
+static inline unsigned int
+v4l2_hevc_pps_num_tile_rows(const struct v4l2_ctrl_hevc_pps *pps)
+{
+	return min_t(unsigned int, pps->num_tile_rows_minus1 + 1,
+		     ARRAY_SIZE(pps->row_height_minus1));
+}
+
+#endif /* _MEDIA_V4L2_HEVC_H */
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 4/9] media: rkvdec: bound HEVC tile loops and PPS id to the array capacity
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
                   ` (2 preceding siblings ...)
  2026-06-17  2:19 ` [PATCH v3 3/9] media: hevc: add bounded tile-count helpers Michael Bommarito
@ 2026-06-17  2:19 ` Michael Bommarito
  2026-06-17  2:19 ` [PATCH v3 5/9] media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity Michael Bommarito
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:19 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

compute_tiles_uniform() and compute_tiles_non_uniform() loop over
num_tile_columns_minus1 + 1 / num_tile_rows_minus1 + 1 entries, and
assemble_hw_pps() writes one COLUMN_WIDTH / ROW_HEIGHT register per tile
and indexes priv_tbl->param_set[] by pic_parameter_set_id, all taken from
the untrusted PPS. Use the bounded v4l2_hevc_pps_num_tile_columns() /
v4l2_hevc_pps_num_tile_rows() helpers for the tile loops, and bail out of
assemble_hw_pps() before indexing priv_tbl->param_set[] with an
out-of-range pic_parameter_set_id, so the writes stay within the hardware
tables.

Fixes: 3595375c2301 ("media: rkvdec: Add HEVC backend")
Fixes: c9a59dc2acc7 ("media: rkvdec: Add HEVC support for the VDPU381 variant")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 .../platform/rockchip/rkvdec/rkvdec-hevc-common.c  | 14 ++++++++++----
 .../media/platform/rockchip/rkvdec/rkvdec-hevc.c   |  7 +++++--
 .../platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c |  2 ++
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
index 3119f3bc9f98b..753aef3aee51e 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc-common.c
@@ -16,6 +16,7 @@
  */
 
 #include <linux/v4l2-common.h>
+#include <media/v4l2-hevc.h>
 #include <media/v4l2-mem2mem.h>
 
 #include "rkvdec.h"
@@ -37,15 +38,17 @@ void compute_tiles_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size,
 			   s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
 {
 	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
+	unsigned int num_cols = v4l2_hevc_pps_num_tile_columns(pps);
+	unsigned int num_rows = v4l2_hevc_pps_num_tile_rows(pps);
 	int i;
 
-	for (i = 0; i < pps->num_tile_columns_minus1 + 1; i++)
+	for (i = 0; i < num_cols; i++)
 		column_width[i] = ((i + 1) * pic_in_cts_width) /
 				  (pps->num_tile_columns_minus1 + 1) -
 				  (i * pic_in_cts_width) /
 				  (pps->num_tile_columns_minus1 + 1);
 
-	for (i = 0; i < pps->num_tile_rows_minus1 + 1; i++)
+	for (i = 0; i < num_rows; i++)
 		row_height[i] = ((i + 1) * pic_in_cts_height) /
 				(pps->num_tile_rows_minus1 + 1) -
 				(i * pic_in_cts_height) /
@@ -57,17 +60,20 @@ void compute_tiles_non_uniform(struct rkvdec_hevc_run *run, u16 log2_min_cb_size
 			       s32 pic_in_cts_height, u16 *column_width, u16 *row_height)
 {
 	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
+	unsigned int num_cols = v4l2_hevc_pps_num_tile_columns(pps);
+	unsigned int num_rows = v4l2_hevc_pps_num_tile_rows(pps);
 	s32 sum = 0;
 	int i;
 
-	for (i = 0; i < pps->num_tile_columns_minus1; i++) {
+	/* The last tile entry is written after the loop, so iterate one less. */
+	for (i = 0; i < num_cols - 1; i++) {
 		column_width[i] = pps->column_width_minus1[i] + 1;
 		sum += column_width[i];
 	}
 	column_width[i] = pic_in_cts_width - sum;
 
 	sum = 0;
-	for (i = 0; i < pps->num_tile_rows_minus1; i++) {
+	for (i = 0; i < num_rows - 1; i++) {
 		row_height[i] = pps->row_height_minus1[i] + 1;
 		sum += row_height[i];
 	}
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
index ac8b825d080a2..568746dae9a61 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-hevc.c
@@ -12,6 +12,7 @@
  *	Jeffy Chen <jeffy.chen@rock-chips.com>
  */
 
+#include <media/v4l2-hevc.h>
 #include <media/v4l2-mem2mem.h>
 
 #include "rkvdec.h"
@@ -156,6 +157,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
 	 * packet unit). so the driver copy SPS/PPS information to the exact PPS
 	 * packet unit for HW accessing.
 	 */
+	if (pps->pic_parameter_set_id >= ARRAY_SIZE(priv_tbl->param_set))
+		return;
 	hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
 	memset(hw_ps, 0, sizeof(*hw_ps));
 
@@ -274,9 +277,9 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
 
 	if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED) {
 		/* Userspace also provide column width and row height for uniform spacing */
-		for (i = 0; i <= pps->num_tile_columns_minus1; i++)
+		for (i = 0; i < v4l2_hevc_pps_num_tile_columns(pps); i++)
 			WRITE_PPS(pps->column_width_minus1[i], COLUMN_WIDTH(i));
-		for (i = 0; i <= pps->num_tile_rows_minus1; i++)
+		for (i = 0; i < v4l2_hevc_pps_num_tile_rows(pps); i++)
 			WRITE_PPS(pps->row_height_minus1[i], ROW_HEIGHT(i));
 	} else {
 		WRITE_PPS(((sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y) - 1,
diff --git a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
index fe6414a175510..6dafa1dd28507 100644
--- a/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
+++ b/drivers/media/platform/rockchip/rkvdec/rkvdec-vdpu381-hevc.c
@@ -145,6 +145,8 @@ static void assemble_hw_pps(struct rkvdec_ctx *ctx,
 	 * packet unit). so the driver copy SPS/PPS information to the exact PPS
 	 * packet unit for HW accessing.
 	 */
+	if (pps->pic_parameter_set_id >= ARRAY_SIZE(priv_tbl->param_set))
+		return;
 	hw_ps = &priv_tbl->param_set[pps->pic_parameter_set_id];
 	memset(hw_ps, 0, sizeof(*hw_ps));
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 5/9] media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
                   ` (3 preceding siblings ...)
  2026-06-17  2:19 ` [PATCH v3 4/9] media: rkvdec: bound HEVC tile loops and PPS id to the array capacity Michael Bommarito
@ 2026-06-17  2:19 ` Michael Bommarito
  2026-09-03  6:51   ` Benjamin Gaignard
  2026-06-17  2:19 ` [PATCH v3 6/9] media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer Michael Bommarito
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:19 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

prepare_tile_info_buffer() writes one entry per tile into the tile_sizes
DMA buffer, sized for a grid equal to the PPS uAPI array capacity. Use the
bounded v4l2_hevc_pps_num_tile_columns() / v4l2_hevc_pps_num_tile_rows()
helpers so the loops stay inside the buffer.

Fixes: cb5dd5a0fa51 ("media: hantro: Introduce G2/HEVC decoder")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
index e8c2e83379def..e7a7c7a42467a 100644
--- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
+++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
@@ -5,6 +5,8 @@
  * Copyright (C) 2020 Safran Passenger Innovations LLC
  */
 
+#include <media/v4l2-hevc.h>
+
 #include "hantro_hw.h"
 #include "hantro_g2_regs.h"
 
@@ -15,8 +17,8 @@ static void prepare_tile_info_buffer(struct hantro_ctx *ctx)
 	const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps;
 	const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
 	u16 *p = (u16 *)((u8 *)ctx->hevc_dec.tile_sizes.cpu);
-	unsigned int num_tile_rows = pps->num_tile_rows_minus1 + 1;
-	unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1;
+	unsigned int num_tile_rows = v4l2_hevc_pps_num_tile_rows(pps);
+	unsigned int num_tile_cols = v4l2_hevc_pps_num_tile_columns(pps);
 	unsigned int pic_width_in_ctbs, pic_height_in_ctbs;
 	unsigned int max_log2_ctb_size, ctb_size;
 	bool tiles_enabled, uniform_spacing;
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 6/9] media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
                   ` (4 preceding siblings ...)
  2026-06-17  2:19 ` [PATCH v3 5/9] media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity Michael Bommarito
@ 2026-06-17  2:19 ` Michael Bommarito
  2026-09-03  6:52   ` Benjamin Gaignard
  2026-06-17  2:19 ` [PATCH v3 7/9] media: verisilicon: rockchip: reject AV1 frames exceeding the tile capacity Michael Bommarito
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:19 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

rockchip_vpu981_av1_dec_set_tile_info() divides context_update_tile_id by
tile_info->tile_cols and writes one descriptor per tile into the tile_info
DMA buffer, which holds AV1_MAX_TILES entries; tile_cols and tile_rows
come from the bitstream. Guard the division against a zero tile_cols by
initialising the context-update values to zero and computing them only
when tile_cols is non-zero, and stop the descriptor writes once the
tile_info buffer is full. The tile geometry written to the hardware
registers is left unmodified; the per-dimension and total tile bounds are
enforced by the control validation.

Fixes: 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 .../verisilicon/rockchip_vpu981_hw_av1_dec.c  | 32 +++++++++++++++----
 1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
index e4e21ad373233..fd00dbd79fe46 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
@@ -578,16 +578,30 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
 	const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
 	const struct v4l2_ctrl_av1_tile_group_entry *group_entry =
 	    ctrls->tile_group_entry;
-	int context_update_y =
-	    tile_info->context_update_tile_id / tile_info->tile_cols;
-	int context_update_x =
-	    tile_info->context_update_tile_id % tile_info->tile_cols;
-	int context_update_tile_id =
-	    context_update_x * tile_info->tile_rows + context_update_y;
+	int context_update_y = 0;
+	int context_update_x = 0;
+	int context_update_tile_id = 0;
 	u8 *dst = av1_dec->tile_info.cpu;
+	u8 *dst_end = dst + av1_dec->tile_info.size;
 	struct hantro_dev *vpu = ctx->dev;
 	int tile0, tile1;
 
+	/*
+	 * tile_cols and tile_rows are bounded by the V4L2 control validation
+	 * (V4L2_AV1_MAX_TILE_{COLS,ROWS} and V4L2_AV1_MAX_TILE_COUNT). Guard
+	 * the divisor here, and keep the descriptor writes within the
+	 * AV1_MAX_TILES tile_info buffer below; the register values use the
+	 * unmodified tile geometry.
+	 */
+	if (tile_info->tile_cols) {
+		context_update_y =
+		    tile_info->context_update_tile_id / tile_info->tile_cols;
+		context_update_x =
+		    tile_info->context_update_tile_id % tile_info->tile_cols;
+		context_update_tile_id =
+		    context_update_x * tile_info->tile_rows + context_update_y;
+	}
+
 	memset(dst, 0, av1_dec->tile_info.size);
 
 	for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) {
@@ -598,6 +612,10 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
 			    tile_info->height_in_sbs_minus_1[tile1] + 1;
 			u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1;
 
+			/* Stop once the tile_info descriptor buffer is full. */
+			if (dst + 16 > dst_end)
+				break;
+
 			/* tile size in SB units (width,height) */
 			*dst++ = x0;
 			*dst++ = 0;
@@ -622,6 +640,8 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
 			*dst++ = (end >> 16) & 255;
 			*dst++ = (end >> 24) & 255;
 		}
+		if (dst + 16 > dst_end)
+			break;
 	}
 
 	hantro_reg_write(vpu, &av1_multicore_expect_context_update, !!(context_update_x == 0));
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 7/9] media: verisilicon: rockchip: reject AV1 frames exceeding the tile capacity
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
                   ` (5 preceding siblings ...)
  2026-06-17  2:19 ` [PATCH v3 6/9] media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer Michael Bommarito
@ 2026-06-17  2:19 ` Michael Bommarito
  2026-09-03  6:53   ` Benjamin Gaignard
  2026-06-17  2:19 ` [PATCH v3 8/9] media: mediatek: vcodec: bound AV1 tile-start copy to the array capacity Michael Bommarito
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:19 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

rockchip_vpu981_av1_dec_set_tile_info() indexes the tile group entry
array by tile1 * tile_cols + tile0, reading up to tile_cols * tile_rows
entries, lays out one descriptor per tile in the AV1_MAX_TILES tile_info
buffer, and programs the real tile_cols / tile_rows into the hardware.

The tile group entry control is a dynamic array sized to the number of
entries userspace submitted, independent of tile_cols / tile_rows, so a
frame that claims more tiles than entries reads past the array. A frame
that claims more than AV1_MAX_TILES tiles also leaves the hardware
programmed for more tiles than the descriptor buffer holds.

Reject both in prepare_run(): tile_cols * tile_rows must not exceed the
submitted entry count or AV1_MAX_TILES. The entry count is read via
v4l2_ctrl_find() (ctrl->elems). This mirrors the bound the mediatek AV1
decoder already enforces.

Fixes: 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 .../verisilicon/rockchip_vpu981_hw_av1_dec.c  | 25 ++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
index fd00dbd79fe46..00aa566a4ccdb 100644
--- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
+++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
@@ -431,20 +431,39 @@ static int rockchip_vpu981_av1_dec_prepare_run(struct hantro_ctx *ctx)
 {
 	struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
 	struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
+	const struct v4l2_av1_tile_info *tile_info;
+	struct v4l2_ctrl *tge;
+	u32 num_tiles;
 
 	ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
 	if (WARN_ON(!ctrls->sequence))
 		return -EINVAL;
 
-	ctrls->tile_group_entry =
-	    hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
-	if (WARN_ON(!ctrls->tile_group_entry))
+	tge = v4l2_ctrl_find(&ctx->ctrl_handler,
+			     V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
+	if (WARN_ON(!tge))
 		return -EINVAL;
+	ctrls->tile_group_entry = tge->p_cur.p;
 
 	ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME);
 	if (WARN_ON(!ctrls->frame))
 		return -EINVAL;
 
+	/*
+	 * rockchip_vpu981_av1_dec_set_tile_info() indexes the tile group
+	 * entry array by tile1 * tile_cols + tile0, so it reads up to
+	 * tile_cols * tile_rows entries, and lays out one descriptor per tile
+	 * in the AV1_MAX_TILES tile_info buffer while programming the real
+	 * tile geometry into the hardware. Reject a frame that claims more
+	 * tiles than userspace submitted, or more than the hardware tile
+	 * buffer holds, so the read stays in bounds and the programmed
+	 * geometry matches the descriptors written.
+	 */
+	tile_info = &ctrls->frame->tile_info;
+	num_tiles = (u32)tile_info->tile_cols * tile_info->tile_rows;
+	if (num_tiles > tge->elems || num_tiles > AV1_MAX_TILES)
+		return -EINVAL;
+
 	ctrls->film_grain =
 	    hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN);
 
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 8/9] media: mediatek: vcodec: bound AV1 tile-start copy to the array capacity
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
                   ` (6 preceding siblings ...)
  2026-06-17  2:19 ` [PATCH v3 7/9] media: verisilicon: rockchip: reject AV1 frames exceeding the tile capacity Michael Bommarito
@ 2026-06-17  2:19 ` Michael Bommarito
  2026-06-17  2:19 ` [PATCH v3 9/9] media: v4l2-ctrls: add KUnit tests for compound control tile validation Michael Bommarito
  2026-07-16  1:03 ` [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Nicolas Dufresne
  9 siblings, 0 replies; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:19 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

vdec_av1_slice_setup_tile() copies tile_cols + 1 / tile_rows + 1 entries
into mi_col_starts[] / mi_row_starts[] from the bitstream tile_info. Bound
the copy to the array capacity.

Fixes: 0934d3759615 ("media: mediatek: vcodec: separate decoder and encoder")
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 .../mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c       | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
index 2d622e85f8271..49d9b4a72387e 100644
--- a/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
+++ b/drivers/media/platform/mediatek/vcodec/decoder/vdec/vdec_av1_req_lat_if.c
@@ -1299,11 +1299,12 @@ static void vdec_av1_slice_setup_tile(struct vdec_av1_slice_frame *frame,
 	tile->uniform_tile_spacing_flag =
 		BIT_FLAG(ctrl_tile, V4L2_AV1_TILE_INFO_FLAG_UNIFORM_TILE_SPACING);
 
-	for (i = 0; i < tile->tile_cols + 1; i++)
+	/* Bound the copy to the mi_col_starts[]/mi_row_starts[] capacity. */
+	for (i = 0; i < tile->tile_cols + 1 && i < V4L2_AV1_MAX_TILE_COLS + 1; i++)
 		tile->mi_col_starts[i] =
 			ALIGN(ctrl_tile->mi_col_starts[i], BIT(mib_size_log2)) >> mib_size_log2;
 
-	for (i = 0; i < tile->tile_rows + 1; i++)
+	for (i = 0; i < tile->tile_rows + 1 && i < V4L2_AV1_MAX_TILE_ROWS + 1; i++)
 		tile->mi_row_starts[i] =
 			ALIGN(ctrl_tile->mi_row_starts[i], BIT(mib_size_log2)) >> mib_size_log2;
 }
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v3 9/9] media: v4l2-ctrls: add KUnit tests for compound control tile validation
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
                   ` (7 preceding siblings ...)
  2026-06-17  2:19 ` [PATCH v3 8/9] media: mediatek: vcodec: bound AV1 tile-start copy to the array capacity Michael Bommarito
@ 2026-06-17  2:19 ` Michael Bommarito
  2026-07-16  1:03 ` [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Nicolas Dufresne
  9 siblings, 0 replies; 17+ messages in thread
From: Michael Bommarito @ 2026-06-17  2:19 UTC (permalink / raw)
  To: Hans Verkuil, Mauro Carvalho Chehab, Sakari Ailus,
	Nicolas Dufresne
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

Add KUnit coverage for the HEVC and AV1 tile-count checks in
std_validate_compound(): in-range counts pass, out-of-range per-dimension
counts and an AV1 grid whose product exceeds V4L2_AV1_MAX_TILE_COUNT are
rejected, and the zero-initialised AV1 frame control that userspace
submits still passes.

Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 drivers/media/v4l2-core/Kconfig               |  12 ++
 .../media/v4l2-core/v4l2-ctrls-core-test.c    | 145 ++++++++++++++++++
 drivers/media/v4l2-core/v4l2-ctrls-core.c     |   4 +
 3 files changed, 161 insertions(+)
 create mode 100644 drivers/media/v4l2-core/v4l2-ctrls-core-test.c

diff --git a/drivers/media/v4l2-core/Kconfig b/drivers/media/v4l2-core/Kconfig
index d50ccac9733cc..52c00dfe9322f 100644
--- a/drivers/media/v4l2-core/Kconfig
+++ b/drivers/media/v4l2-core/Kconfig
@@ -3,6 +3,18 @@
 # Generic video config states
 #
 
+config V4L2_CTRLS_KUNIT_TEST
+	bool "KUnit tests for V4L2 compound control validation" if !KUNIT_ALL_TESTS
+	depends on VIDEO_DEV && KUNIT=y
+	default KUNIT_ALL_TESTS
+	help
+	  This builds KUnit tests for the stateless-codec compound control
+	  validation in std_validate_compound(). They check that out-of-range
+	  HEVC and AV1 tile counts are rejected before the stateless decoders
+	  consume them as loop bounds and array indices.
+
+	  If unsure, say N.
+
 config VIDEO_V4L2_I2C
 	bool
 	depends on I2C && VIDEO_DEV
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core-test.c b/drivers/media/v4l2-core/v4l2-ctrls-core-test.c
new file mode 100644
index 0000000000000..c0141f3defa82
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core-test.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * KUnit tests for HEVC/AV1 tile-count validation in std_validate_compound().
+ * #included at the end of v4l2-ctrls-core.c to reach the static helper.
+ */
+
+#include <kunit/test.h>
+
+static int call_validate_compound(enum v4l2_ctrl_type type, void *payload,
+				  u32 elem_size)
+{
+	struct v4l2_ctrl ctrl = {
+		.type = type,
+		.elem_size = elem_size,
+	};
+	union v4l2_ctrl_ptr ptr = { .p = payload };
+
+	return std_validate_compound(&ctrl, 0, ptr);
+}
+
+/* HEVC PPS: num_tile_columns_minus1 / num_tile_rows_minus1 bounds. */
+static void v4l2_ctrls_hevc_pps_tile_cols(struct kunit *test)
+{
+	struct v4l2_ctrl_hevc_pps *pps;
+
+	pps = kunit_kzalloc(test, sizeof(*pps), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, pps);
+
+	pps->flags = V4L2_HEVC_PPS_FLAG_TILES_ENABLED;
+
+	/* In range: count == array capacity (minus1 == capacity - 1). */
+	pps->num_tile_columns_minus1 = ARRAY_SIZE(pps->column_width_minus1) - 1;
+	pps->num_tile_rows_minus1 = ARRAY_SIZE(pps->row_height_minus1) - 1;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_HEVC_PPS, pps,
+					       sizeof(*pps)),
+			0);
+
+	/* Out of range: one past the column array. */
+	pps->num_tile_columns_minus1 = ARRAY_SIZE(pps->column_width_minus1);
+	pps->num_tile_rows_minus1 = 0;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_HEVC_PPS, pps,
+					       sizeof(*pps)),
+			-EINVAL);
+
+	/* Out of range: maximal attacker value. */
+	pps->num_tile_columns_minus1 = 0xff;
+	pps->num_tile_rows_minus1 = 0xff;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_HEVC_PPS, pps,
+					       sizeof(*pps)),
+			-EINVAL);
+}
+
+/* AV1 frame: tile_cols / tile_rows upper bounds. */
+static void v4l2_ctrls_av1_frame_tile(struct kunit *test)
+{
+	struct v4l2_ctrl_av1_frame *f;
+
+	f = kunit_kzalloc(test, sizeof(*f), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, f);
+
+	/*
+	 * Benign control: a zero-initialised frame (tile_cols == 0) must
+	 * still pass. Userspace and v4l2-compliance set the zeroed default,
+	 * and the divisor that a zero tile_cols would feed is guarded in the
+	 * consuming driver rather than rejected here.
+	 */
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_AV1_FRAME, f,
+					       sizeof(*f)),
+			0);
+
+	/* In range: a 1x1 tiling. */
+	f->tile_info.tile_cols = 1;
+	f->tile_info.tile_rows = 1;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_AV1_FRAME, f,
+					       sizeof(*f)),
+			0);
+
+	/*
+	 * In range: total tiles == V4L2_AV1_MAX_TILE_COUNT with each
+	 * dimension at or below its per-dimension maximum (64 * 8 == 512).
+	 */
+	f->tile_info.tile_cols = V4L2_AV1_MAX_TILE_COLS;
+	f->tile_info.tile_rows = V4L2_AV1_MAX_TILE_COUNT / V4L2_AV1_MAX_TILE_COLS;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_AV1_FRAME, f,
+					       sizeof(*f)),
+			0);
+
+	/*
+	 * Out of range: each dimension is legal on its own but the product
+	 * exceeds V4L2_AV1_MAX_TILE_COUNT (64 * 64 == 4096 > 512), which would
+	 * overflow the per-tile descriptor buffers.
+	 */
+	f->tile_info.tile_cols = V4L2_AV1_MAX_TILE_COLS;
+	f->tile_info.tile_rows = V4L2_AV1_MAX_TILE_ROWS;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_AV1_FRAME, f,
+					       sizeof(*f)),
+			-EINVAL);
+
+	/* Out of range: tile_cols past the array. */
+	f->tile_info.tile_cols = V4L2_AV1_MAX_TILE_COLS + 1;
+	f->tile_info.tile_rows = 1;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_AV1_FRAME, f,
+					       sizeof(*f)),
+			-EINVAL);
+
+	/* Out of range: maximal attacker value. */
+	f->tile_info.tile_cols = 0xff;
+	f->tile_info.tile_rows = 0xff;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_AV1_FRAME, f,
+					       sizeof(*f)),
+			-EINVAL);
+
+	/* Out of range: tile_rows past the array. */
+	f->tile_info.tile_cols = 1;
+	f->tile_info.tile_rows = V4L2_AV1_MAX_TILE_ROWS + 1;
+	KUNIT_EXPECT_EQ(test,
+			call_validate_compound(V4L2_CTRL_TYPE_AV1_FRAME, f,
+					       sizeof(*f)),
+			-EINVAL);
+}
+
+static struct kunit_case v4l2_ctrls_test_cases[] = {
+	KUNIT_CASE(v4l2_ctrls_hevc_pps_tile_cols),
+	KUNIT_CASE(v4l2_ctrls_av1_frame_tile),
+	{}
+};
+
+static struct kunit_suite v4l2_ctrls_test_suite = {
+	.name = "v4l2-ctrls-compound",
+	.test_cases = v4l2_ctrls_test_cases,
+};
+
+kunit_test_suite(v4l2_ctrls_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for V4L2 stateless-codec compound control validation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
index fb20ad13dfec7..1409b06eee0a8 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -2860,3 +2860,7 @@ int v4l2_ctrl_new_fwnode_properties(struct v4l2_ctrl_handler *hdl,
 	return hdl->error;
 }
 EXPORT_SYMBOL(v4l2_ctrl_new_fwnode_properties);
+
+#if IS_ENABLED(CONFIG_V4L2_CTRLS_KUNIT_TEST)
+#include "v4l2-ctrls-core-test.c"
+#endif
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts
  2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
                   ` (8 preceding siblings ...)
  2026-06-17  2:19 ` [PATCH v3 9/9] media: v4l2-ctrls: add KUnit tests for compound control tile validation Michael Bommarito
@ 2026-07-16  1:03 ` Nicolas Dufresne
  9 siblings, 0 replies; 17+ messages in thread
From: Nicolas Dufresne @ 2026-07-16  1:03 UTC (permalink / raw)
  To: Michael Bommarito, Hans Verkuil, Mauro Carvalho Chehab,
	Sakari Ailus
  Cc: Laurent Pinchart, Benjamin Gaignard, Detlev Casanova,
	Ezequiel Garcia, Yunfei Dong, Jonas Karlman, Heiko Stuebner,
	Kees Cook, linux-media, linux-rockchip, linux-mediatek,
	linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5368 bytes --]

Hi,

there was a problem with patchwork regarding that series. Mind trying to resent
to see if it can fix it ?

Nicolas

Le mardi 16 juin 2026 à 22:18 -0400, Michael Bommarito a écrit :
> The stateless HEVC and AV1 controls carry tile counts that several SoC
> decoder drivers consume as loop bounds and array indices when laying out
> fixed-size hardware descriptor buffers. std_validate_compound() does not
> bound them, so a crafted HEVC PPS or AV1 frame control can drive
> out-of-bounds writes and an AV1 divide-by-zero in the rkvdec, hantro,
> rockchip and mediatek decoders.
> 
>   1-2  reject out-of-range HEVC and AV1 tile counts in
>        std_validate_compound() (one patch per codec). For AV1 the per-
>        dimension bound is V4L2_AV1_MAX_TILE_{COLS,ROWS} and the total is
>        V4L2_AV1_MAX_TILE_COUNT.
>   3    add <media/v4l2-hevc.h> with bounded tile-count helpers.
>   4-5  use the helpers in rkvdec and hantro instead of open-coding the
>        clamp; rkvdec also bails before indexing the hardware
>        parameter-set table with an out-of-range HEVC PPS id.
>   6    guard the rockchip VPU981 AV1 divisor against tile_cols == 0 and
>        keep the descriptor writes inside the AV1_MAX_TILES buffer.
>   7    reject a rockchip AV1 frame whose tile_cols * tile_rows exceeds the
>        submitted tile group entry count or the AV1_MAX_TILES descriptor
>        capacity, which set_tile_info() would otherwise read past or leave
>        under-described while programming the larger geometry.
>   8    bound the mediatek AV1 tile-start copy.
>   9    KUnit coverage for the tile-count validation.
> 
> Changes since v2:
>   - Split the combined HEVC+AV1 validation into one patch per codec, each
>     with a single Fixes tag (Benjamin Gaignard).
>   - Move the AV1 total-tile bound into validate_av1_tile_info() using the
>     uAPI V4L2_AV1_MAX_TILE_COUNT, instead of clamping tile_cols/tile_rows
>     in the rockchip driver, which would have corrupted the values written
>     to the hardware registers (Benjamin Gaignard's NACK on v2 4/6).
>   - Add <media/v4l2-hevc.h> with shared bounded tile-count helpers so
>     rkvdec and hantro no longer duplicate the clamp (Benjamin Gaignard).
>   - New patch 7: reject a rockchip AV1 frame that claims more tiles than
>     the submitted tile group entry array holds (set_tile_info() indexes
>     it by tile_cols * tile_rows) or more than AV1_MAX_TILES (the hardware
>     descriptor buffer), which would otherwise leave the hardware
>     programmed for more tiles than the buffer describes. mediatek already
>     guards the entry count; rockchip now guards both.
> 
> checkpatch --strict: 0 errors on all nine. Patches 3 and 9 each carry one
> "added file ... does MAINTAINERS need updating?" warning for the new
> <media/v4l2-hevc.h> and the KUnit test file; both already fall under the
> existing include/media/ and drivers/media/v4l2-core/ MAINTAINERS entries,
> so no MAINTAINERS change is needed. (checkpatch's SPDX sub-check did not
> run in my environment -- spdxcheck.py needs python3-ply -- but the SPDX
> headers are present on both new files.)
> 
> The tile-count validation is exercised with KUnit (patch 9): in-range
> HEVC/AV1 counts pass, out-of-range per-dimension counts and an AV1 grid
> whose product exceeds V4L2_AV1_MAX_TILE_COUNT are rejected, and the
> zero-initialised AV1 frame control that v4l2-compliance and existing
> userspace submit still passes.
> 
> v2:
> https://lore.kernel.org/all/20260614155609.3107600-1-michael.bommarito@gmail.com/
> 
> Michael Bommarito (9):
>   media: v4l2-ctrls: validate HEVC tile counts
>   media: v4l2-ctrls: validate AV1 tile counts
>   media: hevc: add bounded tile-count helpers
>   media: rkvdec: bound HEVC tile loops and PPS id to the array capacity
>   media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer
>     capacity
>   media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer
>   media: verisilicon: rockchip: reject AV1 frames exceeding the tile
>     capacity
>   media: mediatek: vcodec: bound AV1 tile-start copy to the array
>     capacity
>   media: v4l2-ctrls: add KUnit tests for compound control tile
>     validation
> 
>  .../vcodec/decoder/vdec/vdec_av1_req_lat_if.c |   5 +-
>  .../rockchip/rkvdec/rkvdec-hevc-common.c      |  14 +-
>  .../platform/rockchip/rkvdec/rkvdec-hevc.c    |   7 +-
>  .../rockchip/rkvdec/rkvdec-vdpu381-hevc.c     |   2 +
>  .../platform/verisilicon/hantro_g2_hevc_dec.c |   6 +-
>  .../verisilicon/rockchip_vpu981_hw_av1_dec.c  |  57 +++++--
>  drivers/media/v4l2-core/Kconfig               |  12 ++
>  .../media/v4l2-core/v4l2-ctrls-core-test.c    | 145 ++++++++++++++++++
>  drivers/media/v4l2-core/v4l2-ctrls-core.c     |  36 +++++
>  include/media/v4l2-hevc.h                     |  41 +++++
>  10 files changed, 306 insertions(+), 19 deletions(-)
>  create mode 100644 drivers/media/v4l2-core/v4l2-ctrls-core-test.c
>  create mode 100644 include/media/v4l2-hevc.h
> 
> 
> base-commit: e24a98d6884a6e4203a77a94f070a59fcab95208

[-- Attachment #2: This is a digitally signed message part --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 1/9] media: v4l2-ctrls: validate HEVC tile counts
  2026-06-17  2:18 ` [PATCH v3 1/9] media: v4l2-ctrls: validate HEVC " Michael Bommarito
@ 2026-09-03  6:50   ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2026-09-03  6:50 UTC (permalink / raw)
  To: Michael Bommarito, Hans Verkuil, Mauro Carvalho Chehab,
	Sakari Ailus, Nicolas Dufresne
  Cc: Laurent Pinchart, Detlev Casanova, Ezequiel Garcia, Yunfei Dong,
	Jonas Karlman, Heiko Stuebner, Kees Cook, linux-media,
	linux-rockchip, linux-mediatek, linux-kernel


Le 17/06/2026 à 04:18, Michael Bommarito a écrit :
> The stateless HEVC decoders read num_tile_columns_minus1 + 1 entries from
> column_width_minus1[] and num_tile_rows_minus1 + 1 from row_height_minus1[]
> and use them as tile-loop bounds, but std_validate_compound() does not
> bound these u8 counts. Reject a V4L2_CTRL_TYPE_HEVC_PPS with tiling
> enabled whose tile counts exceed the uAPI array capacity, mirroring the
> existing compound-control range checks.
>
> Fixes: 256fa3920874 ("media: v4l: Add definitions for HEVC stateless decoding")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>

Reviewed-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

> ---
>   drivers/media/v4l2-core/v4l2-ctrls-core.c | 12 ++++++++++++
>   1 file changed, 12 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index 6b375720e395c..6d478e1a5ef22 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -1242,6 +1242,18 @@ static int std_validate_compound(const struct v4l2_ctrl *ctrl, u32 idx,
>   
>   			p_hevc_pps->flags &=
>   				~V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED;
> +		} else {
> +			/*
> +			 * These count the entries the stateless HEVC drivers
> +			 * read from column_width_minus1[] / row_height_minus1[]
> +			 * and use as tile-loop bounds.
> +			 */
> +			if (p_hevc_pps->num_tile_columns_minus1 >=
> +			    ARRAY_SIZE(p_hevc_pps->column_width_minus1))
> +				return -EINVAL;
> +			if (p_hevc_pps->num_tile_rows_minus1 >=
> +			    ARRAY_SIZE(p_hevc_pps->row_height_minus1))
> +				return -EINVAL;
>   		}
>   
>   		if (p_hevc_pps->flags &

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 2/9] media: v4l2-ctrls: validate AV1 tile counts
  2026-06-17  2:18 ` [PATCH v3 2/9] media: v4l2-ctrls: validate AV1 " Michael Bommarito
@ 2026-09-03  6:50   ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2026-09-03  6:50 UTC (permalink / raw)
  To: Michael Bommarito, Hans Verkuil, Mauro Carvalho Chehab,
	Sakari Ailus, Nicolas Dufresne
  Cc: Laurent Pinchart, Detlev Casanova, Ezequiel Garcia, Yunfei Dong,
	Jonas Karlman, Heiko Stuebner, Kees Cook, linux-media,
	linux-rockchip, linux-mediatek, linux-kernel


Le 17/06/2026 à 04:18, Michael Bommarito a écrit :
> The stateless AV1 decoders use tile_info.tile_cols and tile_rows as loop
> bounds and as indices into the mi_*_starts[] and *_in_sbs_minus_1[]
> arrays, as the divisor for context_update_tile_id, and their product
> bounds the per-tile descriptor buffers, but std_validate_compound() does
> not bound these u8 fields. Reject a V4L2_CTRL_TYPE_AV1_FRAME whose
> tile_cols or tile_rows exceeds V4L2_AV1_MAX_TILE_COLS / _ROWS, or whose
> product exceeds V4L2_AV1_MAX_TILE_COUNT. A zero tile count is left to the
> consuming driver so the zero-initialised control that existing userspace
> submits is still accepted.
>
> Fixes: 9de30f579980 ("media: Add AV1 uAPI")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>

Reviewed-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

> ---
>   drivers/media/v4l2-core/v4l2-ctrls-core.c | 20 ++++++++++++++++++++
>   1 file changed, 20 insertions(+)
>
> diff --git a/drivers/media/v4l2-core/v4l2-ctrls-core.c b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> index 6d478e1a5ef22..fb20ad13dfec7 100644
> --- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
> +++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
> @@ -790,10 +790,30 @@ static int validate_av1_film_grain(struct v4l2_ctrl_av1_film_grain *fg)
>   	return 0;
>   }
>   
> +static int validate_av1_tile_info(struct v4l2_av1_tile_info *t)
> +{
> +	/*
> +	 * tile_cols and tile_rows index the per-tile descriptor arrays and
> +	 * bound the tile loops in the stateless AV1 drivers; the product
> +	 * bounds the total tile descriptor count.
> +	 */
> +	if (t->tile_cols > V4L2_AV1_MAX_TILE_COLS ||
> +	    t->tile_rows > V4L2_AV1_MAX_TILE_ROWS)
> +		return -EINVAL;
> +
> +	if ((u32)t->tile_cols * t->tile_rows > V4L2_AV1_MAX_TILE_COUNT)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
>   static int validate_av1_frame(struct v4l2_ctrl_av1_frame *f)
>   {
>   	int ret = 0;
>   
> +	ret = validate_av1_tile_info(&f->tile_info);
> +	if (ret)
> +		return ret;
>   	ret = validate_av1_quantization(&f->quantization);
>   	if (ret)
>   		return ret;

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 3/9] media: hevc: add bounded tile-count helpers
  2026-06-17  2:19 ` [PATCH v3 3/9] media: hevc: add bounded tile-count helpers Michael Bommarito
@ 2026-09-03  6:51   ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2026-09-03  6:51 UTC (permalink / raw)
  To: Michael Bommarito, Hans Verkuil, Mauro Carvalho Chehab,
	Sakari Ailus, Nicolas Dufresne
  Cc: Laurent Pinchart, Detlev Casanova, Ezequiel Garcia, Yunfei Dong,
	Jonas Karlman, Heiko Stuebner, Kees Cook, linux-media,
	linux-rockchip, linux-mediatek, linux-kernel


Le 17/06/2026 à 04:19, Michael Bommarito a écrit :
> The stateless HEVC decoders compute the number of tile columns and rows
> from num_tile_columns_minus1 / num_tile_rows_minus1 and clamp it to the
> column_width_minus1[] / row_height_minus1[] capacity before using it as a
> loop bound. Add shared helpers in a new <media/v4l2-hevc.h> so the rkvdec
> and hantro drivers do not each open-code the min_t() clamp.
>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>

Reviewed-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

> ---
>   include/media/v4l2-hevc.h | 41 +++++++++++++++++++++++++++++++++++++++
>   1 file changed, 41 insertions(+)
>   create mode 100644 include/media/v4l2-hevc.h
>
> diff --git a/include/media/v4l2-hevc.h b/include/media/v4l2-hevc.h
> new file mode 100644
> index 0000000000000..973c96be16be4
> --- /dev/null
> +++ b/include/media/v4l2-hevc.h
> @@ -0,0 +1,41 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Helper functions for HEVC stateless codecs.
> + */
> +
> +#ifndef _MEDIA_V4L2_HEVC_H
> +#define _MEDIA_V4L2_HEVC_H
> +
> +#include <linux/minmax.h>
> +#include <media/v4l2-ctrls.h>
> +
> +/**
> + * v4l2_hevc_pps_num_tile_columns - number of HEVC tile columns, bounded
> + * @pps: the V4L2 HEVC PPS control
> + *
> + * Return the number of tile columns (num_tile_columns_minus1 + 1) clamped to
> + * the capacity of column_width_minus1[]. The control validation already
> + * rejects out-of-range counts; this keeps the consuming drivers bounded too.
> + */
> +static inline unsigned int
> +v4l2_hevc_pps_num_tile_columns(const struct v4l2_ctrl_hevc_pps *pps)
> +{
> +	return min_t(unsigned int, pps->num_tile_columns_minus1 + 1,
> +		     ARRAY_SIZE(pps->column_width_minus1));
> +}
> +
> +/**
> + * v4l2_hevc_pps_num_tile_rows - number of HEVC tile rows, bounded
> + * @pps: the V4L2 HEVC PPS control
> + *
> + * Return the number of tile rows (num_tile_rows_minus1 + 1) clamped to the
> + * capacity of row_height_minus1[].
> + */
> +static inline unsigned int
> +v4l2_hevc_pps_num_tile_rows(const struct v4l2_ctrl_hevc_pps *pps)
> +{
> +	return min_t(unsigned int, pps->num_tile_rows_minus1 + 1,
> +		     ARRAY_SIZE(pps->row_height_minus1));
> +}
> +
> +#endif /* _MEDIA_V4L2_HEVC_H */

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 5/9] media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity
  2026-06-17  2:19 ` [PATCH v3 5/9] media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity Michael Bommarito
@ 2026-09-03  6:51   ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2026-09-03  6:51 UTC (permalink / raw)
  To: Michael Bommarito, Hans Verkuil, Mauro Carvalho Chehab,
	Sakari Ailus, Nicolas Dufresne
  Cc: Laurent Pinchart, Detlev Casanova, Ezequiel Garcia, Yunfei Dong,
	Jonas Karlman, Heiko Stuebner, Kees Cook, linux-media,
	linux-rockchip, linux-mediatek, linux-kernel


Le 17/06/2026 à 04:19, Michael Bommarito a écrit :
> prepare_tile_info_buffer() writes one entry per tile into the tile_sizes
> DMA buffer, sized for a grid equal to the PPS uAPI array capacity. Use the
> bounded v4l2_hevc_pps_num_tile_columns() / v4l2_hevc_pps_num_tile_rows()
> helpers so the loops stay inside the buffer.
>
> Fixes: cb5dd5a0fa51 ("media: hantro: Introduce G2/HEVC decoder")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>

Reviewed-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

> ---
>   drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c | 6 ++++--
>   1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
> index e8c2e83379def..e7a7c7a42467a 100644
> --- a/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
> +++ b/drivers/media/platform/verisilicon/hantro_g2_hevc_dec.c
> @@ -5,6 +5,8 @@
>    * Copyright (C) 2020 Safran Passenger Innovations LLC
>    */
>   
> +#include <media/v4l2-hevc.h>
> +
>   #include "hantro_hw.h"
>   #include "hantro_g2_regs.h"
>   
> @@ -15,8 +17,8 @@ static void prepare_tile_info_buffer(struct hantro_ctx *ctx)
>   	const struct v4l2_ctrl_hevc_pps *pps = ctrls->pps;
>   	const struct v4l2_ctrl_hevc_sps *sps = ctrls->sps;
>   	u16 *p = (u16 *)((u8 *)ctx->hevc_dec.tile_sizes.cpu);
> -	unsigned int num_tile_rows = pps->num_tile_rows_minus1 + 1;
> -	unsigned int num_tile_cols = pps->num_tile_columns_minus1 + 1;
> +	unsigned int num_tile_rows = v4l2_hevc_pps_num_tile_rows(pps);
> +	unsigned int num_tile_cols = v4l2_hevc_pps_num_tile_columns(pps);
>   	unsigned int pic_width_in_ctbs, pic_height_in_ctbs;
>   	unsigned int max_log2_ctb_size, ctb_size;
>   	bool tiles_enabled, uniform_spacing;

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 6/9] media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer
  2026-06-17  2:19 ` [PATCH v3 6/9] media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer Michael Bommarito
@ 2026-09-03  6:52   ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2026-09-03  6:52 UTC (permalink / raw)
  To: Michael Bommarito, Hans Verkuil, Mauro Carvalho Chehab,
	Sakari Ailus, Nicolas Dufresne
  Cc: Laurent Pinchart, Detlev Casanova, Ezequiel Garcia, Yunfei Dong,
	Jonas Karlman, Heiko Stuebner, Kees Cook, linux-media,
	linux-rockchip, linux-mediatek, linux-kernel


Le 17/06/2026 à 04:19, Michael Bommarito a écrit :
> rockchip_vpu981_av1_dec_set_tile_info() divides context_update_tile_id by
> tile_info->tile_cols and writes one descriptor per tile into the tile_info
> DMA buffer, which holds AV1_MAX_TILES entries; tile_cols and tile_rows
> come from the bitstream. Guard the division against a zero tile_cols by
> initialising the context-update values to zero and computing them only
> when tile_cols is non-zero, and stop the descriptor writes once the
> tile_info buffer is full. The tile geometry written to the hardware
> registers is left unmodified; the per-dimension and total tile bounds are
> enforced by the control validation.
>
> Fixes: 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>

Reviewed-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

> ---
>   .../verisilicon/rockchip_vpu981_hw_av1_dec.c  | 32 +++++++++++++++----
>   1 file changed, 26 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> index e4e21ad373233..fd00dbd79fe46 100644
> --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> @@ -578,16 +578,30 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
>   	const struct v4l2_av1_tile_info *tile_info = &ctrls->frame->tile_info;
>   	const struct v4l2_ctrl_av1_tile_group_entry *group_entry =
>   	    ctrls->tile_group_entry;
> -	int context_update_y =
> -	    tile_info->context_update_tile_id / tile_info->tile_cols;
> -	int context_update_x =
> -	    tile_info->context_update_tile_id % tile_info->tile_cols;
> -	int context_update_tile_id =
> -	    context_update_x * tile_info->tile_rows + context_update_y;
> +	int context_update_y = 0;
> +	int context_update_x = 0;
> +	int context_update_tile_id = 0;
>   	u8 *dst = av1_dec->tile_info.cpu;
> +	u8 *dst_end = dst + av1_dec->tile_info.size;
>   	struct hantro_dev *vpu = ctx->dev;
>   	int tile0, tile1;
>   
> +	/*
> +	 * tile_cols and tile_rows are bounded by the V4L2 control validation
> +	 * (V4L2_AV1_MAX_TILE_{COLS,ROWS} and V4L2_AV1_MAX_TILE_COUNT). Guard
> +	 * the divisor here, and keep the descriptor writes within the
> +	 * AV1_MAX_TILES tile_info buffer below; the register values use the
> +	 * unmodified tile geometry.
> +	 */
> +	if (tile_info->tile_cols) {
> +		context_update_y =
> +		    tile_info->context_update_tile_id / tile_info->tile_cols;
> +		context_update_x =
> +		    tile_info->context_update_tile_id % tile_info->tile_cols;
> +		context_update_tile_id =
> +		    context_update_x * tile_info->tile_rows + context_update_y;
> +	}
> +
>   	memset(dst, 0, av1_dec->tile_info.size);
>   
>   	for (tile0 = 0; tile0 < tile_info->tile_cols; tile0++) {
> @@ -598,6 +612,10 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
>   			    tile_info->height_in_sbs_minus_1[tile1] + 1;
>   			u32 x0 = tile_info->width_in_sbs_minus_1[tile0] + 1;
>   
> +			/* Stop once the tile_info descriptor buffer is full. */
> +			if (dst + 16 > dst_end)
> +				break;
> +
>   			/* tile size in SB units (width,height) */
>   			*dst++ = x0;
>   			*dst++ = 0;
> @@ -622,6 +640,8 @@ static void rockchip_vpu981_av1_dec_set_tile_info(struct hantro_ctx *ctx)
>   			*dst++ = (end >> 16) & 255;
>   			*dst++ = (end >> 24) & 255;
>   		}
> +		if (dst + 16 > dst_end)
> +			break;
>   	}
>   
>   	hantro_reg_write(vpu, &av1_multicore_expect_context_update, !!(context_update_x == 0));

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v3 7/9] media: verisilicon: rockchip: reject AV1 frames exceeding the tile capacity
  2026-06-17  2:19 ` [PATCH v3 7/9] media: verisilicon: rockchip: reject AV1 frames exceeding the tile capacity Michael Bommarito
@ 2026-09-03  6:53   ` Benjamin Gaignard
  0 siblings, 0 replies; 17+ messages in thread
From: Benjamin Gaignard @ 2026-09-03  6:53 UTC (permalink / raw)
  To: Michael Bommarito, Hans Verkuil, Mauro Carvalho Chehab,
	Sakari Ailus, Nicolas Dufresne
  Cc: Laurent Pinchart, Detlev Casanova, Ezequiel Garcia, Yunfei Dong,
	Jonas Karlman, Heiko Stuebner, Kees Cook, linux-media,
	linux-rockchip, linux-mediatek, linux-kernel


Le 17/06/2026 à 04:19, Michael Bommarito a écrit :
> rockchip_vpu981_av1_dec_set_tile_info() indexes the tile group entry
> array by tile1 * tile_cols + tile0, reading up to tile_cols * tile_rows
> entries, lays out one descriptor per tile in the AV1_MAX_TILES tile_info
> buffer, and programs the real tile_cols / tile_rows into the hardware.
>
> The tile group entry control is a dynamic array sized to the number of
> entries userspace submitted, independent of tile_cols / tile_rows, so a
> frame that claims more tiles than entries reads past the array. A frame
> that claims more than AV1_MAX_TILES tiles also leaves the hardware
> programmed for more tiles than the descriptor buffer holds.
>
> Reject both in prepare_run(): tile_cols * tile_rows must not exceed the
> submitted entry count or AV1_MAX_TILES. The entry count is read via
> v4l2_ctrl_find() (ctrl->elems). This mirrors the bound the mediatek AV1
> decoder already enforces.
>
> Fixes: 727a400686a2 ("media: verisilicon: Add Rockchip AV1 decoder")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>

Reviewed-by: Benjamin Gaignard <benjamin.gaignard@collabora.com>

> ---
>   .../verisilicon/rockchip_vpu981_hw_av1_dec.c  | 25 ++++++++++++++++---
>   1 file changed, 22 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> index fd00dbd79fe46..00aa566a4ccdb 100644
> --- a/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> +++ b/drivers/media/platform/verisilicon/rockchip_vpu981_hw_av1_dec.c
> @@ -431,20 +431,39 @@ static int rockchip_vpu981_av1_dec_prepare_run(struct hantro_ctx *ctx)
>   {
>   	struct hantro_av1_dec_hw_ctx *av1_dec = &ctx->av1_dec;
>   	struct hantro_av1_dec_ctrls *ctrls = &av1_dec->ctrls;
> +	const struct v4l2_av1_tile_info *tile_info;
> +	struct v4l2_ctrl *tge;
> +	u32 num_tiles;
>   
>   	ctrls->sequence = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_SEQUENCE);
>   	if (WARN_ON(!ctrls->sequence))
>   		return -EINVAL;
>   
> -	ctrls->tile_group_entry =
> -	    hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
> -	if (WARN_ON(!ctrls->tile_group_entry))
> +	tge = v4l2_ctrl_find(&ctx->ctrl_handler,
> +			     V4L2_CID_STATELESS_AV1_TILE_GROUP_ENTRY);
> +	if (WARN_ON(!tge))
>   		return -EINVAL;
> +	ctrls->tile_group_entry = tge->p_cur.p;
>   
>   	ctrls->frame = hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FRAME);
>   	if (WARN_ON(!ctrls->frame))
>   		return -EINVAL;
>   
> +	/*
> +	 * rockchip_vpu981_av1_dec_set_tile_info() indexes the tile group
> +	 * entry array by tile1 * tile_cols + tile0, so it reads up to
> +	 * tile_cols * tile_rows entries, and lays out one descriptor per tile
> +	 * in the AV1_MAX_TILES tile_info buffer while programming the real
> +	 * tile geometry into the hardware. Reject a frame that claims more
> +	 * tiles than userspace submitted, or more than the hardware tile
> +	 * buffer holds, so the read stays in bounds and the programmed
> +	 * geometry matches the descriptors written.
> +	 */
> +	tile_info = &ctrls->frame->tile_info;
> +	num_tiles = (u32)tile_info->tile_cols * tile_info->tile_rows;
> +	if (num_tiles > tge->elems || num_tiles > AV1_MAX_TILES)
> +		return -EINVAL;
> +
>   	ctrls->film_grain =
>   	    hantro_get_ctrl(ctx, V4L2_CID_STATELESS_AV1_FILM_GRAIN);
>   

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-09-03  6:53 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-17  2:18 [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Michael Bommarito
2026-06-17  2:18 ` [PATCH v3 1/9] media: v4l2-ctrls: validate HEVC " Michael Bommarito
2026-09-03  6:50   ` Benjamin Gaignard
2026-06-17  2:18 ` [PATCH v3 2/9] media: v4l2-ctrls: validate AV1 " Michael Bommarito
2026-09-03  6:50   ` Benjamin Gaignard
2026-06-17  2:19 ` [PATCH v3 3/9] media: hevc: add bounded tile-count helpers Michael Bommarito
2026-09-03  6:51   ` Benjamin Gaignard
2026-06-17  2:19 ` [PATCH v3 4/9] media: rkvdec: bound HEVC tile loops and PPS id to the array capacity Michael Bommarito
2026-06-17  2:19 ` [PATCH v3 5/9] media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity Michael Bommarito
2026-09-03  6:51   ` Benjamin Gaignard
2026-06-17  2:19 ` [PATCH v3 6/9] media: verisilicon: rockchip: guard VPU981 AV1 divisor and tile buffer Michael Bommarito
2026-09-03  6:52   ` Benjamin Gaignard
2026-06-17  2:19 ` [PATCH v3 7/9] media: verisilicon: rockchip: reject AV1 frames exceeding the tile capacity Michael Bommarito
2026-09-03  6:53   ` Benjamin Gaignard
2026-06-17  2:19 ` [PATCH v3 8/9] media: mediatek: vcodec: bound AV1 tile-start copy to the array capacity Michael Bommarito
2026-06-17  2:19 ` [PATCH v3 9/9] media: v4l2-ctrls: add KUnit tests for compound control tile validation Michael Bommarito
2026-07-16  1:03 ` [PATCH v3 0/9] media: bound stateless HEVC/AV1 tile counts Nicolas Dufresne

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox