From: Michael Bommarito <michael.bommarito@gmail.com>
To: Hans Verkuil <hverkuil@kernel.org>,
Mauro Carvalho Chehab <mchehab@kernel.org>,
Sakari Ailus <sakari.ailus@linux.intel.com>,
Nicolas Dufresne <nicolas.dufresne@collabora.com>
Cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
Benjamin Gaignard <benjamin.gaignard@collabora.com>,
Detlev Casanova <detlev.casanova@collabora.com>,
Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>,
Yunfei Dong <yunfei.dong@mediatek.com>,
Jonas Karlman <jonas@kwiboo.se>, Heiko Stuebner <heiko@sntech.de>,
Kees Cook <kees@kernel.org>,
linux-media@vger.kernel.org, linux-rockchip@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 6/6] media: v4l2-ctrls: add KUnit tests for compound control tile validation
Date: Sun, 14 Jun 2026 11:56:08 -0400 [thread overview]
Message-ID: <20260614155609.3107600-7-michael.bommarito@gmail.com> (raw)
In-Reply-To: <20260614155609.3107600-1-michael.bommarito@gmail.com>
Drive std_validate_compound() with crafted HEVC PPS and AV1 frame controls
and assert that out-of-range tile counts are rejected with -EINVAL while
in-range values pass, including the zero-initialised AV1 frame default that
userspace and v4l2-compliance submit. The tests are #included at the end of
v4l2-ctrls-core.c to reach the static helper, gated by
CONFIG_V4L2_CTRLS_KUNIT_TEST.
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-8
---
drivers/media/v4l2-core/Kconfig | 12 ++
.../media/v4l2-core/v4l2-ctrls-core-test.c | 130 ++++++++++++++++++
drivers/media/v4l2-core/v4l2-ctrls-core.c | 4 +
3 files changed, 146 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..04b15d860a0af 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/AV1 tile counts and parameter-set / tile indices are rejected
+ before driver code consumes them as array indices or loop bounds.
+
+ 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..813872694eb46
--- /dev/null
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core-test.c
@@ -0,0 +1,130 @@
+// 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: maximal legal tile_cols / tile_rows. */
+ 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)),
+ 0);
+
+ /* 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 58e2eb7002a19..5e86b6373a136 100644
--- a/drivers/media/v4l2-core/v4l2-ctrls-core.c
+++ b/drivers/media/v4l2-core/v4l2-ctrls-core.c
@@ -2851,3 +2851,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
prev parent reply other threads:[~2026-06-14 15:56 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-14 15:56 [PATCH v2 0/6] media: v4l2-ctrls: bound stateless HEVC/AV1 tile counts Michael Bommarito
2026-06-14 15:56 ` [PATCH v2 1/6] media: v4l2-ctrls: validate HEVC and AV1 " Michael Bommarito
2026-06-14 15:56 ` [PATCH v2 2/6] media: rkvdec: bound HEVC tile loops and PPS id to the array capacity Michael Bommarito
2026-06-14 15:56 ` [PATCH v2 3/6] media: verisilicon: hantro: bound G2 HEVC tile loop to the buffer capacity Michael Bommarito
2026-06-14 15:56 ` [PATCH v2 4/6] media: verisilicon: rockchip: bound VPU981 AV1 tile loop and guard divisor Michael Bommarito
2026-06-14 15:56 ` [PATCH v2 5/6] media: mediatek: vcodec: bound AV1 tile-start copy to the array capacity Michael Bommarito
2026-06-14 15:56 ` Michael Bommarito [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=20260614155609.3107600-7-michael.bommarito@gmail.com \
--to=michael.bommarito@gmail.com \
--cc=benjamin.gaignard@collabora.com \
--cc=detlev.casanova@collabora.com \
--cc=ezequiel@vanguardiasur.com.ar \
--cc=heiko@sntech.de \
--cc=hverkuil@kernel.org \
--cc=jonas@kwiboo.se \
--cc=kees@kernel.org \
--cc=laurent.pinchart@ideasonboard.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-rockchip@lists.infradead.org \
--cc=mchehab@kernel.org \
--cc=nicolas.dufresne@collabora.com \
--cc=sakari.ailus@linux.intel.com \
--cc=yunfei.dong@mediatek.com \
/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