Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Sofus Forstreuter" <sofus.c@icloud.com>
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org,
	media-ci@linuxtv.org
Subject: Re: [PATCH v3 11/17] arm64: dts: apple: t8103: add avd nodes
Date: Tue, 20 Oct 2026 13:27:26 +0000	[thread overview]
Message-ID: <sashiko-outbox-179022@kernel.org> (raw)
In-Reply-To: <20261020-avd-v3-11-85cbaa2125d8@icloud.com>

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] media: apple: avd: out-of-bounds memory accesses in HEVC tile handling
- [Critical] media: apple: avd: integer overflow and OOB access in HEVC slice handling
- [Critical] media: apple: avd: out-of-bounds accesses and error return in avd-h264.c
- [Critical] media: apple: avd: missing bounds and NULL checks in AV1 and VP9 decoders
- [High] media: apple: avd: swapped hdiv and vdiv in fill_comp()
--

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

arm64: dts: apple: t8103: add avd nodes

This patch adds the Apple AVD DART IOMMU and video-codec device tree nodes
for the Apple T8103 SoC, enabling the AVD V4L2 stateless video decoder.

> diff --git a/arch/arm64/boot/dts/apple/t8103.dtsi b/arch/arm64/boot/dts/apple/t8103.dtsi
> index 5b68bf0df4e6b..530290e7cb32e 100644
> --- a/arch/arm64/boot/dts/apple/t8103.dtsi
> +++ b/arch/arm64/boot/dts/apple/t8103.dtsi
> @@ -984,6 +984,38 @@ pinctrl_aop: pinctrl@24a820000 {
>  				     <AIC_IRQ 274 IRQ_TYPE_LEVEL_HIGH>;
>  		};
>  
> +		avd_dart: iommu@269010000 {
> +			compatible = "apple,t8103-dart";
> +			reg = <0x2 0x69010000 0x0 0x4000>;
> +			interrupt-parent = <&aic>;
> +			interrupts = <AIC_IRQ 547 IRQ_TYPE_LEVEL_HIGH>;
> +			#iommu-cells = <1>;
> +			power-domains = <&ps_avd_sys>;
> +		};
> +
> +		video-codec@269070000 {
> +			compatible = "apple,t8103-avd";

[Severity: Critical]
Enabling the video-codec@269070000 node exposes the AVD V4L2 M2M stateless
decoder to userspace. Can crafted HEVC controls trigger out-of-bounds memory
accesses and division by zero in drivers/media/platform/apple/avd/avd-hevc.c
through avd_device_run() -> avd_hevc_run()?

First, struct v4l2_ctrl_hevc_pps defines column_width_minus1[20] and
row_height_minus1[22], and std_validate_compound() in
drivers/media/v4l2-core/v4l2-ctrls-core.c does not bound
num_tile_columns_minus1 or num_tile_rows_minus1 when
V4L2_HEVC_PPS_FLAG_TILES_ENABLED is set. However, avd_hevc_validate_pps()
allows up to HEVC_MAX_TILE_COLS (40) and HEVC_MAX_TILE_ROWS (44):

drivers/media/platform/apple/avd/avd-hevc.c:avd_hevc_validate_pps() {
	if (pps->num_tile_columns_minus1 + 1 > HEVC_MAX_TILE_COLS ||
	    pps->num_tile_rows_minus1 + 1 > HEVC_MAX_TILE_ROWS)
		return -EINVAL;
}

When compute_tiles_non_uniform() iterates up to pps->num_tile_columns_minus1
and pps->num_tile_rows_minus1:

drivers/media/platform/apple/avd/avd-hevc.c:compute_tiles_non_uniform() {
	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;

	sum = 0;
	for (i = 0; i < pps->num_tile_rows_minus1; i++) {
		row_height[i] = MIN(pps->row_height_minus1[i] + 1,
				    pic_in_cts_height);
		sum += row_height[i];
	}
	row_height[i] = pic_in_cts_height - sum;
}

Doesn't this read past pps->column_width_minus1[20] and
pps->row_height_minus1[22] (and past the end of struct v4l2_ctrl_hevc_pps)?

Second, in compute_tiles_non_uniform() above, each entry is only clamped
individually to pic_in_cts_width or pic_in_cts_height. If sum exceeds
pic_in_cts_width or pic_in_cts_height, won't the u16 assignment to
column_width[i] or row_height[i] underflow to a large positive value, causing
compute_bd() to produce col_bd and row_bd entries larger than
pic_in_ctbs_width and pic_in_ctbs_height, which then overflows the heap-
allocated ctb_addr_rs_to_ts and tile_ids arrays in compute_rs_to_ts() and
compute_tile_ids()?

drivers/media/platform/apple/avd/avd-hevc.c:compute_tile_ids() {
	for (j = 0, tile_idx = 0; j <= pps->num_tile_rows_minus1; j++)
		for (i = 0; i <= pps->num_tile_columns_minus1; i++, tile_idx++)
			for (y = row_bd[j]; y < row_bd[j + 1]; y++)
				for (x = col_bd[i]; x < col_bd[i + 1]; x++)
					tile_ids[ctb_addr_rs_to_ts
							 [y * pic_in_ctbs_width +
							  x]] = tile_idx;
}

Third, avd_hevc_validate_sps() does not reject zero
pic_width_in_luma_samples / pic_height_in_luma_samples or out-of-range
log2_min_luma_coding_block_size_minus3 /
log2_diff_max_min_luma_coding_block_size. In avd_hevc_compute_tiles():

drivers/media/platform/apple/avd/avd-hevc.c:avd_hevc_compute_tiles() {
	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_height * pic_in_ctbs_width;
}

Can this shift out of bounds and divide by zero before avd_hevc_alloc_scratch()
checks if (!max_cu_width), or result in ZERO_SIZE_PTR dereferences when width
or height is zero?

[Severity: Critical]
In drivers/media/platform/apple/avd/avd-hevc.c, can avd_hevc_run_preamble()
and stream_slices() also overflow when validating and iterating HEVC slice
segments?

In avd_hevc_run_preamble(), sum is a 32-bit signed int accumulating __u32
num_entry_point_offsets across up to 1800 slices:

drivers/media/platform/apple/avd/avd-hevc.c:avd_hevc_run_preamble() {
	for (i = 0; i < run->num_slices; i++)
		sum += (*run->sl)[i].num_entry_point_offsets;

	if (sum && sum != run->num_entry_point_offsets)
		return -EINVAL;
}

Since std_validate_compound() does not bound num_entry_point_offsets, can
crafted values (for example, sl[0].num_entry_point_offsets = 2 and
sl[1].num_entry_point_offsets = 0xfffffffe) overflow sum to 0 or a small
value matching run->num_entry_point_offsets? That causes avd_init_job() to
allocate only run.num_slices + run.num_entry_point_offsets + 1 segments in
ctx->job.segments while stream_slices() loops sl->num_entry_point_offsets + 1
times, reading past (*run->entry_point_offsets) and writing past
ctx->job.segments via push() and avd_end_segment().

Additionally, in stream_slices(), pos is incremented for every segment with
NEW_TILE_ID:

drivers/media/platform/apple/avd/avd-hevc.c:stream_slices() {
			row = pos / num_cols;
			col = pos % num_cols;
}

and passed to submit_slice_segment():

drivers/media/platform/apple/avd/avd-hevc.c:submit_slice_segment() {
		if (pps->flags & V4L2_HEVC_PPS_FLAG_TILES_ENABLED)
			push(AVD_SL_DIM_END_ROW((hflip ? 4 : 0) |
						(vflip ? 8 : 0)) |
				     AVD_SL_DIM_END_COL(col) |
				     AVD_SL_DIM_END_Y(row_bd[row + 1] - 1) |
				     AVD_SL_DIM_END_X(col_bd[col + 1] - 1),
			     "cm3_set_ctb_xy");
}

Without checking that row <= pps->num_tile_rows_minus1, won't pos / num_cols
exceed HEVC_MAX_TILE_ROWS and read row_bd[row] and row_bd[row + 1] out of
bounds on the stack?

[Severity: Critical]
In drivers/media/platform/apple/avd/avd-h264.c (enabled on T8103 via
AVD_CAPABILITY_H264), are there also out-of-bounds accesses and an uninitialized
error return reachable from userspace?

1. In stream_slice(), sl->ref_pic_list1[0].index is an unvalidated u8 from
V4L2_CID_STATELESS_H264_SLICE_PARAMS (not checked by std_validate_compound()
in drivers/media/v4l2-core/v4l2-ctrls-core.c):

drivers/media/platform/apple/avd/avd-h264.c:stream_slice() {
		/* sl->ref_pic_list1[0].index < ARRAY_SIZE(decode->dpb) */
		/* bidirectional reference of previous mv */
		ref = avd_get_ref_buf(
			ctx, &dst->base.vb,
			decode->dpb[sl->ref_pic_list1[0].index].reference_ts);
}

Doesn't this read past the 16-entry decode->dpb array when index >= 16?

2. In avd_h264_realloc_slices(), when h264_ctx->slice_num reaches
h264_ctx->alloc_slice_num, job->num is already alloc_slice_num + 1 (1 header
segment from stream_hdr() plus alloc_slice_num slice segments from
stream_slice()), matching the allocated capacity of job->buf.cpu:

drivers/media/platform/apple/avd/avd-h264.c:avd_h264_realloc_slices() {
	memcpy(job->segments, job->buf.cpu,
	       sizeof(*job->segments) * (job->num + 1));
}

Won't copying job->num + 1 entries read one struct avd_segment past the end of
job->buf.cpu (and if job->buf.size is already large enough earlier in
avd_h264_realloc_slices(), return 0 without updating job->num_alloc)?

3. In avd_h264_run(), avd_h264_realloc_slices() is only called when
ctx->job.segments is non-NULL:

drivers/media/platform/apple/avd/avd-h264.c:avd_h264_run() {
	if (ctx->job.segments &&
	    h264_ctx->slice_num >= h264_ctx->alloc_slice_num) {
		ret = avd_h264_realloc_slices(ctx);
		if (ret)
			goto postamble;
	}

	h264_ctx->active_slice = &h264_ctx->slices[h264_ctx->slice_num];
}

Since avd_h264_done() only resets h264_ctx->slice_num when
V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF is not set, what happens if userspace
submits slices with first_mb_in_slice != 0 and
V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF set? With ctx->job.segments still NULL,
won't h264_ctx->slice_num increment past h264_ctx->alloc_slice_num and write
past the end of h264_ctx->slices?

4. In avd_h264_start(), ret is 0 after avd_h264_validate_sps() succeeds:

drivers/media/platform/apple/avd/avd-h264.c:avd_h264_start() {
	h264_ctx->slices = kzalloc_objs(*h264_ctx->slices,
					h264_ctx->alloc_slice_num, GFP_KERNEL);
	if (!h264_ctx->slices)
		goto err_free_ctx;
}

If kzalloc_objs() fails here, doesn't avd_h264_start() free h264_ctx, set
ctx->priv = NULL, and return 0 to avd_start_streaming(), causing a NULL
pointer dereference in avd_h264_run()?

[Severity: Critical]
In drivers/media/platform/apple/avd/avd-vp9.c and
drivers/media/platform/apple/avd/avd-av1.c, could several missing bounds and
NULL checks cause crashes or out-of-bounds memory accesses?

1. In avd-vp9.c (enabled on T8103 via AVD_CAPABILITY_VP9), set_tiles() calls
vb2_plane_vaddr() on a VB2_DMABUF-capable queue:

drivers/media/platform/apple/avd/avd-vp9.c:set_tiles() {
	const u8 *data = vb2_plane_vaddr(&src->vb2_buf, 0);
...
				tile_size = get_unaligned_be32(&data[offset]);
}

and in avd-av1.c, avd_av1_set_prob() also calls vb2_plane_vaddr() without
checking for NULL:

drivers/media/platform/apple/avd/avd-av1.c:avd_av1_set_prob() {
	memcpy(vb2_plane_vaddr(&dst->base.vb.vb2_buf, 0) +
		       AVD_AV1_CDFS_OFFSET(dst->base.vb.vb2_buf.planes[0].length,
					   dst->av1.color_size),
	       av1_ctx->bufs.probs.cpu, sizeof(struct avd_av1_cdfs));
}

Since vb2_dc_vaddr() returns NULL if dma_buf_vmap_unlocked() fails or is not
supported by the exporter, can these dereference NULL?

2. In avd_av1_set_prob() above, neither validate_av1_frame() nor avd-av1.c
validates frame->frame_width_minus_1 + 1 and frame->frame_height_minus_1 + 1
against the CAPTURE buffer dimensions. If update_dec_buf_info() computes
dst->av1.color_size larger than dst->base.vb.vb2_buf.planes[0].length, won't
AVD_AV1_CDFS_OFFSET() underflow and cause memcpy() to write out of bounds
before the buffer?

3. In drivers/media/platform/apple/avd/avd-av1.c:set_tiles(), __u32
tile_group->tile_row and tile_group->tile_col are assigned to signed int
before the bounds check:

drivers/media/platform/apple/avd/avd-av1.c:set_tiles() {
			tile_row = tile_group->tile_row;
			tile_col = tile_group->tile_col;

			if (tile_col >= V4L2_AV1_MAX_TILE_COLS ||
			    tile_row >= V4L2_AV1_MAX_TILE_ROWS)
				return -EINVAL;
}

Can values >= 0x80000000 become negative, bypass this check, and index
tile_info arrays out of bounds?

4. In drivers/media/platform/apple/avd/avd-av1.c:set_ref_hdr(),
frame->ref_frame_idx[i] (__s8) is not validated by validate_av1_frame():

drivers/media/platform/apple/avd/avd-av1.c:set_ref_hdr() {
			if (frame->reference_frame_ts[frame->ref_frame_idx[i]] ==
			    frame->reference_frame_ts[frame->ref_frame_idx[j]]) {
}

Can negative or out-of-range ref_frame_idx values index out of bounds of
frame->reference_frame_ts[8]? Similarly, avd_av1_get_ref_buf() checks idx
against V4L2_AV1_TOTAL_REFS_PER_FRAME (8) instead of V4L2_AV1_REFS_PER_FRAME
(7) before indexing frame->ref_frame_idx[7], and avd_av1_set_prob() only
checks primary_ref_frame == 7 rather than >= V4L2_AV1_REFS_PER_FRAME.

5. In drivers/media/platform/apple/avd/avd-av1.c:avd_av1_run_preamble():

drivers/media/platform/apple/avd/avd-av1.c:avd_av1_run_preamble() {
	if (run->frame->tile_info.tile_cols * run->frame->tile_info.tile_rows >
		ctrl->new_elems)
		return -EINVAL;
}

Should this check ctrl->elems instead of ctrl->new_elems when reading from
ctrl->p_cur.p?

6. In drivers/media/platform/apple/avd/avd-av1.c:calc_upscale():

drivers/media/platform/apple/avd/avd-av1.c:calc_upscale() {
	*step_x = ((downscaled_plane_w << SUPERRES_SCALE_BITS) +
		   (upscaled_plane_w / 2)) /
		  upscaled_plane_w;
}

If frame->upscaled_width is 0, won't upscaled_plane_w be 0 and trigger a
division by zero?

7. In drivers/media/platform/apple/avd/avd-av1.c:set_refs(), alpha, beta,
gamma, and delta are declared outside the reference loop:

drivers/media/platform/apple/avd/avd-av1.c:set_refs() {
	s64 alpha = 0, beta = 0, gamma = 0, delta = 0;
}

Since avd_av1_dec_get_shear_params() returns early without clearing its output
parameters when mat[2] <= 0, won't a subsequent reference frame reuse stale
shear parameters from an earlier iteration?

[Severity: High]
In drivers/media/platform/apple/avd/avd-drv.c, are info->vdiv and info->hdiv
swapped when fill_comp() calculates the UV plane size and metadata?

drivers/media/platform/apple/avd/avd-drv.c:fill_comp() {
	/* uv has 16x16 tiles and 8 bytes of metadata per tile */
	calc_tile_meta(width / info->vdiv, height / info->hdiv,
		       v4l2_format_info_bpp(info, 1), 16, 8, &uv,
		       &uv_meta);
}

In drivers/media/v4l2-core/v4l2-common.c:v4l2_format_info(), the 4:2:2 formats
V4L2_PIX_FMT_APPLE_AX16 and V4L2_PIX_FMT_APPLE_AX23 define .hdiv = 2 and
.vdiv = 1. Passing width / info->vdiv and height / info->hdiv to
calc_tile_meta(u32 w, u32 h, ...) divides width by 1 and height by 2 instead
of width / info->hdiv and height / info->vdiv, which miscalculates the UV tile
count, UV metadata offset (comp->offsets[2]), and total buffer size
(comp->size) whenever width or height is an odd multiple of 16.

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261020-avd-v3-0-85cbaa2125d8@icloud.com?part=11

  reply	other threads:[~2026-10-20 13:27 UTC|newest]

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

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=sashiko-outbox-179022@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox