All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Sofus Forstreuter" <sofus.c@icloud.com>
Cc: media-ci@linuxtv.org, conor+dt@kernel.org,
	devicetree@vger.kernel.org, robh@kernel.org
Subject: Re: [PATCH v2 09/17] media: apple: avd: add hevc support
Date: Sat, 26 Sep 2026 13:35:02 +0000	[thread overview]
Message-ID: <20260926133503.276A71F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20260926-avd-v2-9-ecebe6a3648e@icloud.com>

Thank you for your contribution! Sashiko AI review found 9 potential issue(s) to consider:
- [Critical] avd: heap buffer overflow due to missing array bounds validation on pps->column_width_minus1
- [High] avd: kernel panic and buffer overflow via unbounded sps->log2_diff shift
- [High] avd: kernel stack out-of-bounds read via unbounded pos variable in stream_slices
- [High] avd: double out-of-bounds array read in stream_slice_mv via sl->collocated_ref_idx
- [High] avd: heap out-of-bounds read via off-by-one check on slice_segment_addr
- [High] avd: arbitrary physical DMA read via integer underflow in slice size offset validation
- [High] avd: use-after-free and out-of-bounds read due to lockless dereference of V4L2 dynamic array pointers
- [High] avd: integer overflow in entry point offset validation allows heap out-of-bounds read
- [Medium] avd: hardware register corruption due to overlapping bitmasks for HEVC_TR_INTRA and HEVC_TR_INTER
--

commit a7dbbf6a1924ee26ecce4f5c5b73414f86b42959
Author: Sofus Forstreuter <sofus.c@icloud.com>

    media: apple: avd: add hevc support
    
    The fluster score is 143/147 for JCT-VC-HEVC_V1.
    
    RPS_E_qualcomm_5, WPP_D_ericsson_MAIN_2 and WPP_D_ericsson_MAIN10_2 are
    failing, but should be supported.

> diff --git a/drivers/media/platform/apple/avd/avd-hevc.c b/drivers/media/platform/apple/avd/avd-hevc.c
> new file mode 100644
> index 0000000000000..d90c7770006b7
> --- /dev/null
> +++ b/drivers/media/platform/apple/avd/avd-hevc.c
> @@ -0,0 +1,1340 @@

[ ... ]

> +#define HEVC_TR_INTRA(v)	FIELD_PREP(GENMASK(4, 1), v)
> +#define HEVC_TR_INTER(v)	FIELD_PREP(GENMASK(7, 4), v)

[Severity: Medium]
Do these bitmasks overlap? It looks like both GENMASK(4, 1) and GENMASK(7, 4) 
include bit 4. When these are ORed together in set_header() like so:

	HEVC_TR_INTER(sps->max_transform_hierarchy_depth_inter) |
	HEVC_TR_INTRA(sps->max_transform_hierarchy_depth_intra) |

could this clobber the overlapping bit and misconfigure the hardware decoder?

[ ... ]

> +static void stream_slice_mv(struct avd_ctx *ctx, struct avd_hevc_run *run,
> +			    const struct v4l2_ctrl_hevc_slice_params *sl,
> +			    bool is_first)
> +{
> +	const struct v4l2_ctrl_hevc_decode_params *decode = run->decode;
> +	struct avd_decoded_buffer *dst, *ref;
> +	bool ref_valid;
> +	const u8 *ref_list;
> +	int ref_idx = 0;
> +
> +	ref_list = sl->slice_type == V4L2_HEVC_SLICE_TYPE_P ? sl->ref_idx_l0 :
> +		   sl->flags & V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0 ?
> +							      sl->ref_idx_l0 :
> +							      sl->ref_idx_l1;
> +
> +	if (sl->slice_type == V4L2_HEVC_SLICE_TYPE_I) {
> +		push(AVD_OP_SL_REF |
> +			     AVD_OP_SL_REF_SLICE_I(sl->slice_type ==
> +						   V4L2_HEVC_SLICE_TYPE_I),
> +		     "slc_a8c_cmd_ref_type");
> +		return;
> +	}
> +	/* bidirectional prediction */
> +	if (sl->collocated_ref_idx < V4L2_HEVC_DPB_ENTRIES_NUM_MAX &&
> +	    ref_list[sl->collocated_ref_idx] < V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
> +		ref_idx = ref_list[sl->collocated_ref_idx];
> +
> +	dst = vb2_to_avd_decoded_buf(&run->base.bufs.dst->vb2_buf);
> +	ref = avd_get_ref_buf(ctx, &dst->base.vb,
> +			      decode->dpb[ref_idx].timestamp);
> +
> +	ref_valid = !(sl->flags &
> +		      V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT) &&
> +		    (sl->flags &
> +		     V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED) &&
> +		    is_first && !ref->hevc.is_intra;
> +
> +	push(AVD_OP_SL_REF |
> +		     AVD_OP_SL_REF_MAX_MERGE(
> +			     5 - sl->five_minus_max_num_merge_cand) |
> +		     AVD_OP_SL_REF_FLAG_CABAC(
> +			     sl->flags &
> +			     V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT) |
> +		     AVD_OP_SL_REF_FLAG0(
> +			     (sl->flags &
> +			      V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED) &&
> +			     !(sl->flags &
> +			       V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0)) |
> +		     AVD_OP_SL_REF_FLAG1(
> +			     !(sl->flags &
> +			       V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO)) |
> +		     AVD_OP_SL_REF_FLAG2(
> +			     (sl->flags &
> +			      V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED) ||
> +			     (sl->flags &
> +			      V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT)) |
> +		     AVD_OP_SL_REF_NUM_L0(sl->num_ref_idx_l0_active_minus1) |
> +		     AVD_OP_SL_REF_NUM_L1(sl->num_ref_idx_l1_active_minus1) |
> +		     AVD_OP_SL_REF_SLICE_P(sl->slice_type ==
> +					   V4L2_HEVC_SLICE_TYPE_P) |
> +		     AVD_OP_SL_REF_SLICE_B(ref_valid),
> +	     "slc_a8c_cmd_ref_type");
> +
> +	if (ref_valid) {
> +		dma_addr_t mv_color_addr =
> +			vb2_dma_contig_plane_dma_addr(&ref->base.vb.vb2_buf,
> +						      0) +
> +			(ref->base.vb.planes[0].length -
> +			 mv_color_size(fmt_width(ctx), fmt_height(ctx)));
> +		pusha(mv_color_addr, "slc_bd4_sps_tile_addr2_lsb8",
> +		      decode->dpb[ref_list[sl->collocated_ref_idx]]
> +			      .pic_order_cnt_val);

[Severity: High]
Is it possible to have an out-of-bounds read here? 

If sl->collocated_ref_idx (which is controlled by userspace) is >= 16 
(V4L2_HEVC_DPB_ENTRIES_NUM_MAX), it seems to bypass the initial bounds check
which updates ref_idx. Later, when pushing the command:

		      decode->dpb[ref_list[sl->collocated_ref_idx]]

could this read ref_list out-of-bounds, yielding a garbage index, and then 
subsequently use that to read out-of-bounds in decode->dpb, programming 
arbitrary memory values into the hardware?

> +	}
> +}

[ ... ]

> +static int submit_slice_segment(struct avd_ctx *ctx, struct avd_hevc_run *run,
> +				const struct v4l2_ctrl_hevc_slice_params *sl,
> +				int row, int col, u32 col_bd[23],
> +				u32 row_bd[23], u32 pic_in_cts_width,
> +				u32 pic_in_cts_height, bool first_slice,
> +				bool hflip, bool vflip, u32 coded_flags,
> +				u32 last_tile_block)
> +{
> +	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
> +	u32 tb_x, tb_y, tile_block, tile_boundary;
> +
> +	if (coded_flags & NEW_SLICE) {
> +		tb_x = sl->slice_segment_addr % pic_in_cts_width;
> +		tb_y = sl->slice_segment_addr / pic_in_cts_width;
> +
> +		tile_block = AVD_OP_SL_LOC_Y(tb_y) | AVD_OP_SL_LOC_X(tb_x);
> +
> +		if (!(sl->flags &
> +		      V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT))
> +			last_tile_block = tile_block;
> +
> +		/*
> +		 * tile block start
> +		 * CABAC window
> +		 */
> +		push(AVD_OP_SL_LOC | last_tile_block, "cm3_cmd_set_cabac_xy");
> +
> +		stream_slice_dqtblk(ctx, run, sl);
> +	} else {
> +		tile_boundary = AVD_OP_SL_LOC_Y(row_bd[row]) |
> +				AVD_OP_SL_LOC_X(col_bd[col]);

[Severity: High]
Can the 'row' index here exceed the bounds of the 45-element row_bd kernel
stack array?

In stream_slices(), the row index is calculated from an unbounded 'pos' 
counter which increments for each entry point offset:

			row = pos / num_cols;
			col = pos % num_cols;

Since there can be up to 1760 entry point offsets, 'row' is not bounded
against 44. Could this result in an out-of-bounds stack read?

> +	}

[ ... ]

> +static void compute_tiles_non_uniform(struct avd_hevc_run *run,
> +				      u16 log2_min_cb_size, u16 width,
> +				      u16 height, u32 pic_in_cts_width,
> +				      u32 pic_in_cts_height, u16 *column_width,
> +				      u16 *row_height)
> +{
> +	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
> +	u32 sum = 0;
> +	int i;
> +
> +	for (i = 0; i < pps->num_tile_columns_minus1; i++) {
> +		column_width[i] = MIN(pps->column_width_minus1[i] + 1,
> +				      pic_in_cts_width);
> +		sum += column_width[i];
> +	}
> +	column_width[i] = pic_in_cts_width - sum;

[Severity: Critical]
Does this code overflow column_width_minus1[] and also underflow
column_width[i]?

The validation allows pps->num_tile_columns_minus1 up to 39, but the V4L2
payload array column_width_minus1 only has 20 elements. This could cause an
out-of-bounds read of the struct.

Additionally, since sum is not validated, if sum exceeds pic_in_cts_width, 
does pic_in_cts_width - sum underflow into a huge unsigned 16-bit integer? 

This huge dimension seems to propagate into compute_bd() and compute_tile_ids(), 
potentially causing thousands of out-of-bounds memory writes on the kernel heap 
when indexing ctb_addr_rs_to_ts and tile_ids.

> +
> +	sum = 0;

[ ... ]

> +static void stream_slices(struct avd_ctx *ctx, struct avd_hevc_run *run)
> +{
> +	const struct v4l2_ctrl_hevc_sps *sps = run->sps;
> +	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
> +	const struct v4l2_ctrl_hevc_slice_params *sl;
> +	struct avd_hevc_tile_info *tile_info = &run->tile_info;
> +	bool tiles_enabled, first_slice, first_segment;
> +	bool hflip, vflip;
> +	int slice_segment_offset, entry_point_idx = 0, pos = 0, offset = 0;
> +	int row, col, i, s, to;
> +	int slice_flag, size, new_offset;
> +	int tile_id, last_tile_id;
> +	u16 log2_min_cb_size, width, height;
> +	u32 max_cu_width, pic_in_ctbs_width, pic_in_ctbs_height;
> +	u32 pic_in_ctbs_size, num_cols, last_tile_block = 0;
> +	struct sl_ctx last = {
> +		.q1_col = -1,
> +		.q1_row = -1,
> +	};
> +
> +	width = sps->pic_width_in_luma_samples;
> +	height = sps->pic_height_in_luma_samples;
> +
> +	tiles_enabled = !!(pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED);
> +
> +	log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
> +
> +	num_cols = pps->num_tile_columns_minus1 + 1;
> +
> +	max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size +
> +			     log2_min_cb_size);
> +	pic_in_ctbs_width = (width + max_cu_width - 1) / max_cu_width;
> +	pic_in_ctbs_height = (height + max_cu_width - 1) / max_cu_width;
> +	pic_in_ctbs_size = pic_in_ctbs_width * pic_in_ctbs_height;
> +
> +	for (s = 0; s < run->num_slices; s++) {
> +		sl = &(*run->sl)[s];
> +		slice_segment_offset = 0;
> +
> +		/* entry_points are not needed for WPP */
> +		to = tiles_enabled ? sl->num_entry_point_offsets + 1 : 1;
> +
> +		for (i = 0; i < to; i++) {
> +			first_segment = i == 0;
> +			first_slice = s == 0;
> +
> +			if (tiles_enabled && to > 1) {
> +				if (i < sl->num_entry_point_offsets) {
> +					size = (*run->entry_point_offsets)
> +						       [entry_point_idx++];

[Severity: High]
Is it safe to locklessly dereference the V4L2 dynamic array pointer here?

In avd_hevc_run_preamble(), the pointer to the dynamic array control is cached
without acquiring the V4L2 state lock:

	run->entry_point_offsets = ctrl ? (const u32 **)&ctrl->p_cur.p : NULL;
	...
	run->sl = ctrl ?
		(const struct v4l2_ctrl_hevc_slice_params **)&ctrl->p_cur.p
		: NULL;

If a concurrent VIDIOC_S_EXT_CTRLS ioctl reallocates the array to a smaller size
while the decoder is running, *run->entry_point_offsets will point to the new
smaller array, but the cached bounds will remain large, leading to out-of-bounds 
heap reads here. Also, dereferencing p_cur.p concurrently while it is being freed 
could trigger a Use-After-Free.

[Severity: High]
Does this code allow an integer overflow during validation?

In avd_hevc_run_preamble(), the total number of entry points is accumulated
into a signed 32-bit int:

	for (i = 0; i < run->num_slices; i++)
		sum += (*run->sl)[i].num_entry_point_offsets;

If userspace provides extremely large num_entry_point_offsets, could this
sum wrap around and exactly match run->num_entry_point_offsets to bypass the 
validation? If so, entry_point_idx could iterate out-of-bounds and read past 
the end of the allocated entry_point_offsets array here.

> +					new_offset = size;
> +				} else {
> +					size = sl->bit_size / 8 -
> +					       sl->data_byte_offset -
> +					       slice_segment_offset;
> +					new_offset =
> +						size + sl->data_byte_offset;
> +				}
> +			} else {
> +				size = (sl->bit_size) / 8 -
> +				       sl->data_byte_offset;
> +				new_offset = size + sl->data_byte_offset;

[Severity: High]
Can the subtraction here trigger an integer underflow?

Userspace could supply sl->data_byte_offset or entry point offsets such that 
they exceed sl->bit_size / 8. This would cause an underflow, resulting in a 
huge integer for 'size'. 

When set_slice() is called:

	dma_addr_t coded_in =
		run->base.coded_in + offset + sl->data_byte_offset;

this uncontrolled size, alongside an unvalidated memory address, is programmed 
into the hardware, potentially causing it to perform arbitrary DMA memory reads.

> +			}
> +
> +			if (sl->slice_segment_addr > pic_in_ctbs_size)
> +				return;
> +
> +			tile_id = tile_info->tile_ids
> +					  [tile_info->ctb_addr_rs_to_ts
> +						   [sl->slice_segment_addr]];

[Severity: High]
Does this validation allow an off-by-one error?

Because it checks for > rather than >=, could sl->slice_segment_addr exactly 
equal pic_in_ctbs_size, leading to a heap out-of-bounds read of 
ctb_addr_rs_to_ts? The resulting garbage value might then be used to read 
tile_ids out-of-bounds.

> +			last_tile_id =

[ ... ]

> +static int avd_hevc_compute_tiles(struct avd_ctx *ctx, struct avd_hevc_run *run)
> +{
> +	const struct v4l2_ctrl_hevc_sps *sps = run->sps;
> +	const struct v4l2_ctrl_hevc_pps *pps = run->pps;
> +	bool tiles_enabled;
> +	struct avd_hevc_tile_info *tile_info = &run->tile_info;
> +	u16 log2_min_cb_size, width, height;
> +	u32 max_cu_width, pic_in_ctbs_width, pic_in_ctbs_height,
> +		pic_in_ctbs_size;
> +
> +	width = sps->pic_width_in_luma_samples;
> +	height = sps->pic_height_in_luma_samples;
> +
> +	log2_min_cb_size = sps->log2_min_luma_coding_block_size_minus3 + 3;
> +
> +	max_cu_width = 1 << (sps->log2_diff_max_min_luma_coding_block_size +
> +			     log2_min_cb_size);
> +	pic_in_ctbs_width = (width + max_cu_width - 1) / max_cu_width;

[Severity: High]
Does this code permit an unbounded bit shift?

Userspace provides sps->log2_diff_max_min_luma_coding_block_size and 
sps->log2_min_luma_coding_block_size_minus3 without bounds checking. If their
sum exceeds 31, the shift will result in undefined behavior. This could yield 0,
triggering a zero division on the very next line.

Alternatively, if it evaluates to a huge value (like 1 << 30), could this 
overflow the 32-bit math during scratch allocations in avd_hevc_alloc_scratch() 
like so:

	ret = avd_buf_alloc(avd, &hevc_ctx->bufs.ip_above,
			    ((max_col * max_cu_width) / 4) * bit_depth);

causing it to wrap around to a dangerously small size, leading to hardware
memory corruption?

> +	pic_in_ctbs_height = (height + max_cu_width - 1) / max_cu_width;
> +	pic_in_ctbs_size = pic_in_ctbs_height * pic_in_ctbs_width;

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260926-avd-v2-0-ecebe6a3648e@icloud.com?part=9

  reply	other threads:[~2026-09-26 13:35 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-26 13:14 [PATCH v2 00/17] media: apple: add avd driver Sofus Forstreuter
2026-09-26 13:14 ` Sofus Forstreuter
2026-09-26 13:14 ` [PATCH v2 01/17] dt-bindings: media: add apple,avd Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-29  8:46   ` Krzysztof Kozlowski
2026-09-29  8:46     ` Krzysztof Kozlowski
2026-09-26 13:14 ` [PATCH v2 02/17] media: v4l2: Add P210 pixel format Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:14 ` [PATCH v2 03/17] media: v4l2: Add Apple interchange pixel formats Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:14 ` [PATCH v2 04/17] media: v4l2-ctrls: validate av1 tile info Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:29   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 05/17] media: v4l2-ctrls: validate vp9 tile_rows_log2 Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:14 ` [PATCH v2 06/17] media: apple: add avd driver Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:36   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 07/17] media: apple: avd: add h264 support Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:34   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 08/17] media: apple: avd: add vp9 support Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:33   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 09/17] media: apple: avd: add hevc support Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:35   ` sashiko-bot [this message]
2026-09-26 13:14 ` [PATCH v2 10/17] media: apple: avd: add av1 support Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:37   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 11/17] arm64: dts: apple: t8103: add avd nodes Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:14 ` [PATCH v2 12/17] arm64: dts: apple: t8112: " Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:35   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 13/17] arm64: dts: apple: t8122: " Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:29   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 14/17] arm64: dts: apple: t600x: " Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:14 ` [PATCH v2 15/17] arm64: dts: apple: t602x: " Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:14 ` [PATCH v2 16/17] arm64: dts: apple: t6030: " Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:34   ` sashiko-bot
2026-09-26 13:14 ` [PATCH v2 17/17] arm64: dts: apple: t6031: " Sofus Forstreuter
2026-09-26 13:14   ` Sofus Forstreuter
2026-09-26 13:35   ` sashiko-bot
2026-10-05 22:29 ` [PATCH v2 00/17] media: apple: add avd driver Neal Gompa
2026-10-05 22:29   ` Neal Gompa

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=20260926133503.276A71F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=media-ci@linuxtv.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=sofus.c@icloud.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.