Linux-PHY Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Cody Kang" <codykang.hk@gmail.com>
Cc: dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
	conor+dt@kernel.org, linux-phy@lists.infradead.org,
	vkoul@kernel.org, robh@kernel.org, neil.armstrong@linaro.org,
	olteanv@gmail.com
Subject: Re: [PATCH v2 08/17] drm/spacemit: add Saturn DPU core types, cmdlist and display MMU
Date: Sun, 09 Aug 2026 13:32:34 +0000	[thread overview]
Message-ID: <20260809133234.F325B1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260809-k3-display-v2-8-327d7910bf71@gmail.com>

Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [Critical] Modifying the command list of unmodified planes during CRTC commits leads to unbounded growth of `cl->nod_len` and DMA buffer overflow.
- [High] Command list DMA buffers are embedded in transient atomic states, leading to lifecycle conflicts and shared buffer corruption.
- [High] Unconditional dereference of `priv->cmdlist_groups[i]` leads to a NULL pointer dereference in `cmdlist_atomic_commit`.
- [High] Directly accessing `plane->state` and `crtc->state` during atomic commits creates TOCTOU races with subsequent non-blocking commits.
- [High] Hardware DMMU reads out of bounds if the page table entries exceed the pre-allocated buffer size limit.
- [High] NULL pointer dereference on `fb` in the `dpu_plane_info` tracepoint.
- [High] Potential NULL pointer dereference of `a_crtc->crtc.dev` in `cmdlist_regs_packing` if a plane is being disabled.
- [High] Calling `spacemit_dmmu_map` sleeps in an atomic context and introduces data races on the command list.
- [Medium] Unprotected read-modify-write on MMIO registers in the `__dpu_mmio_field_write` macro.
- [Medium] Unvalidated `rdma_id` is used directly as an array index, leading to out-of-bounds writes.
--

--- Patch [8]: [PATCH v2 08/17] drm/spacemit: add Saturn DPU core types, cmdlist and display MMU ---
commit 4c661acb763f61ae82caefcc3ef85c607c0b6ffe
Author: Cody Kang <codykang.hk@gmail.com>

drm/spacemit: add Saturn DPU core types, cmdlist and display MMU

Register programming of the Saturn DPU is batched: the driver builds
per-module register lists in memory and the hardware command-list
engine replays them at frame boundaries, while scanout buffers are
mapped through the DPU's private address translation unit (DMMU)
rather than scanned out by physical address.

> diff --git a/drivers/gpu/drm/spacemit/dpu/dpu_trace.h b/drivers/gpu/drm/spacemit/dpu/dpu_trace.h
> index 0000000000000..abfe34a67a4bd 100644
> --- /dev/null
> +++ b/drivers/gpu/drm/spacemit/dpu/dpu_trace.h
[ ... ]
> +	TP_fast_assign(
> +			__entry->rdma_id = rdma_id;
> +			__entry->src_w = state->src_w >> 16;
> +			__entry->src_h = state->src_h >> 16;
> +			__entry->src_x = state->src_x >> 16;
> +			__entry->src_y = state->src_y >> 16;
> +			__entry->crtc_w = state->crtc_w;
> +			__entry->crtc_h = state->crtc_h;
> +			__entry->crtc_x = state->crtc_x;
> +			__entry->crtc_y = state->crtc_y;
> +			__entry->width = fb->width;
> +			__entry->height = fb->height;
> +			__entry->format = fb->format->format;

[Severity: High]
Is it possible for fb to be NULL here? 

During a plane disable operation in the DRM atomic framework, the plane
state will have a NULL framebuffer. Would this tracepoint cause a NULL
pointer dereference when capturing these fields?

> diff --git a/drivers/gpu/drm/spacemit/spacemit_cmdlist.c b/drivers/gpu/drm/spacemit/spacemit_cmdlist.c
> index 0000000000000..f4f14eaa12a0f 100644
> --- /dev/null
> +++ b/drivers/gpu/drm/spacemit/spacemit_cmdlist.c
[ ... ]
> +void cmdlist_regs_packing(struct cmdlist *cl, enum cmdlist_mode_type mod,
> +			  struct cmdlist_regs *cl_regs)
> +{
[ ... ]
> +	if (cl->type == CMDLIST_PLANE) {
> +		struct spacemit_plane_state *spacemit_pstate =
> +				cl_to_spacemit_pstate(cl);
> +
> +		a_crtc = to_spacemit_crtc(spacemit_pstate->state.crtc);
> +
> +		drm_dbg(a_crtc->crtc.dev, "rch_id = %d, ch_y = %u\n",

[Severity: High]
When a plane is disabled, its state.crtc is set to NULL. Could a_crtc end
up being NULL here, leading to a dereference when accessing
a_crtc->crtc.dev for logging?

> +			spacemit_pstate->rdma_id,
> +			spacemit_pstate->state.crtc_y);
> +	} else if (cl->type == CMDLIST_CRTC) {
[ ... ]
> +void cmdlist_sort_by_group(struct drm_crtc *crtc)
> +{
> +	struct cmdlist *first_cl;
> +	struct cmdlist *last_cl;
> +	struct cmdlist *p;
> +	struct cmdlist *prev;
> +	struct drm_plane *plane;
> +	struct spacemit_crtc_rdma *rdmas =
> +			to_spacemit_crtc_state(crtc->state)->rdmas;
> +	struct spacemit_drm_private *priv = crtc->dev->dev_private;
> +
> +	drm_atomic_crtc_for_each_plane(plane, crtc) {
> +		struct spacemit_plane_state *spacemit_pstate =
> +			to_spacemit_plane_state(plane->state);

[Severity: High]
Does directly accessing the current plane->state create a TOCTOU race
with subsequent non-blocking commits? 

In asynchronous commit tails, the DRM core swaps these pointers. Using
plane->state directly instead of the passed drm_atomic_state might result
in reading uncommitted state.

> +		u32 rdma_id = spacemit_pstate->rdma_id;
> +
> +		first_cl = &spacemit_pstate->cl;
> +		if (!first_cl->va)
> +			continue;
> +
> +		last_cl = first_cl;
> +		last_cl->next = NULL;

[Severity: Critical]
What happens to the command list of unmodified planes during CRTC commits? 

It looks like we mutate last_cl->next for all active planes, even those
not modified by the current commit. Later in cmdlist_atomic_commit(),
cmdlist_fill_conf_row() appends configuration rows to it. Since the plane
state isn't duplicated, does cl->nod_len grow unboundedly on every update
until it overflows the fixed DMA buffer?

> +
> +		rdmas[rdma_id].in_use = true;
> +		if (priv->cmdlist_groups[rdma_id]) {

[Severity: Medium]
Is there a guarantee that rdma_id is within bounds? 

We are using it as an index into rdmas[] and priv->cmdlist_groups[]
without validating it against the hardware limit (hwdev->rdma_nums) or
checking for RDMA_INVALID_ID.

[ ... ]
> +void cmdlist_atomic_commit(struct drm_crtc *crtc,
> +			   struct drm_crtc_state *old_state)
> +{
> +	int i;
> +	struct cmdlist *cur_cl, *first_cl;
> +	struct spacemit_crtc *a_crtc = NULL;
> +	u32 chy, addrl, addrh;
> +	struct spacemit_drm_private *priv = crtc->dev->dev_private;
> +	struct spacemit_hw_device *hwdev = priv->hwdev;
> +	struct spacemit_crtc_rdma *cur_rdmas =
> +			to_spacemit_crtc_state(crtc->state)->rdmas;

[Severity: High]
Similar to the plane state access earlier, does accessing crtc->state
directly create a race condition with asynchronous commits?

> +	struct spacemit_crtc_rdma *old_rdmas =
> +			to_spacemit_crtc_state(old_state)->rdmas;
> +
> +	a_crtc = to_spacemit_crtc(crtc);
> +
> +	for (i = 0; i < hwdev->rdma_nums; i++) {
> +		if (old_rdmas[i].in_use)
> +			hwdev->enable_cmdlist(a_crtc, hwdev, i, false);
> +
> +		if (cur_rdmas[i].in_use) {
> +			drm_dbg(crtc->dev, "cmdlist group = %d\n", i);
> +			cur_cl = priv->cmdlist_groups[i];
> +			first_cl = cur_cl;
> +			while (cur_cl) {
> +				hwdev->cmdlist_fill_conf_row(cur_cl, hwdev,
> +					CMDLIST_CMP_INVALID);
> +				cmdlist_dump_node(cur_cl);
> +				cur_cl = cur_cl->next;
> +			}
> +			chy = cl_to_spacemit_pstate(first_cl)->state.crtc_y;

[Severity: High]
If cmdlist_sort_by_group() skips populating priv->cmdlist_groups[i]
because first_cl->va was NULL, but cur_rdmas[i].in_use remains true
(inherited from the old state before disabling), could cur_cl become NULL?

If so, this would lead to a NULL pointer dereference when accessing
first_cl or priv->cmdlist_groups[i]->pa just below.

> +			addrl = (lower_32_bits(priv->cmdlist_groups[i]->pa) &
> +				 CMDLIST_ADDRL_ALIGN_MASK) >> CMDLIST_ADDRL_ALIGN_BITS;
[ ... ]
> +int spacemit_cmdlist_alloc(struct device *dev, struct cmdlist *cl)
> +{
> +	if (cl->va)
> +		return 0;
> +
> +	cl->size = PER_CMDLIST_SIZE;
> +	cl->va = dma_alloc_coherent(dev, cl->size, &cl->pa,
> +				    GFP_KERNEL | __GFP_ZERO);

[Severity: High]
Should the command list DMA buffer be embedded directly in the transient
plane state? 

By skipping allocation when cl->va is non-NULL, duplicating plane states
results in a shallow copy where all states share the same physical DMA
buffer. Mutating this buffer during a new atomic commit might overwrite
the command list actively being scanned out by the hardware.

> diff --git a/drivers/gpu/drm/spacemit/spacemit_dmmu.c b/drivers/gpu/drm/spacemit/spacemit_dmmu.c
> index 0000000000000..ff4b3378a087c 100644
> --- /dev/null
> +++ b/drivers/gpu/drm/spacemit/spacemit_dmmu.c
[ ... ]
> +static inline void spacemit_dmmu_fill_pgtable(struct dpu_mmu_tbl *tbl,
> +					      struct sg_table *sgt)
> +{
> +	struct sg_dma_page_iter dma_iter;
> +	u32 max_entries = tbl->size / 4;
> +	u32 *ttbr = tbl->va;
> +	u32 i = 0, n = 0, temp = 0;
> +
> +	for_each_sgtable_dma_page(sgt, &dma_iter, 0) {
> +		/*
> +		 * atomic_check sized the table off this same fb; catch a drift
> +		 */
> +		if (WARN_ON_ONCE(n + HW_ALIGN_TTB_NUM >= max_entries))
> +			break;

[Severity: High]
If we hit this WARN_ON_ONCE and break early, we don't communicate the
truncated size back to the caller. 

> +		ttbr[n++] = (sg_page_iter_dma_address(&dma_iter) >> PAGE_SHIFT) & 0x3FFFFFF;
> +	}
[ ... ]
> +int spacemit_dmmu_map(struct drm_framebuffer *fb, struct dpu_mmu_tbl *mmu_tbl,
> +		      u8 tbu_id, struct cmdlist_regs *cl_rdma,
> +		 struct drm_plane *plane)
> +{
> +	struct spacemit_drm_private *priv = fb->dev->dev_private;
> +	struct spacemit_hw_device *hwdev = priv->hwdev;
> +	struct sg_table *sgt = NULL;
> +	struct tbu_instance tbu = { };
> +	u32 val;
> +
> +	sgt = drm_gem_shmem_get_pages_sgt(to_drm_gem_shmem_obj(fb->obj[0]));

[Severity: High]
Is it safe to call this here? 

drm_gem_shmem_get_pages_sgt() performs a sleepable allocation (GFP_KERNEL)
and acquires a mutex. If called during atomic_update (the standard
hardware programming phase), it sleeps in an atomic context.

If called during prepare_fb to avoid the atomic context, does mutating
the cl_rdma buffer below introduce data races with the hardware
processing the previous frame?

> +	if (IS_ERR(sgt))
> +		return PTR_ERR(sgt);
> +
> +	/* Every advertised format is single-planar */
> +	tbu.ttb_pa[0] = mmu_tbl->pa;
> +	tbu.tbu_va[0] = TBU_BASE_VA(tbu_id);
> +	tbu.ttb_size[0] = roundup(fb->obj[0]->size, PAGE_SIZE) >> PAGE_SHIFT;

[Severity: High]
Since tbu.ttb_size[0] is programmed to the full FB size regardless of
whether spacemit_dmmu_fill_pgtable() truncated the table, could this
cause the hardware DMMU to read out of bounds past the physical
allocation of mmu_tbl?

> +
> +	spacemit_dmmu_fill_pgtable(mmu_tbl, sgt);
> +
> +	/*
> +	 * enable = 1, fbc_mode = 0 (linear only)
> +	 * single plane in bits [3:2]
> +	 */
> +	val = 0x1;
> +	/* tbu_burst_limit_en = 1, tlb_fetch_active_en = 1 */
> +	val |= BIT(4) | BIT(5);
> +	val = val | (DPU_QOS_URGENT << 8);
> +	hwdev->rdma_dmmu(hwdev, tbu_id, &tbu, fb, val, cl_rdma, plane);

> diff --git a/drivers/gpu/drm/spacemit/spacemit_dpu_reg.h b/drivers/gpu/drm/spacemit/spacemit_dpu_reg.h
> index 0000000000000..a57ed34d38e2f 100644
> --- /dev/null
> +++ b/drivers/gpu/drm/spacemit/spacemit_dpu_reg.h
[ ... ]
> +#define __dpu_mmio_field_write(__addr, __mask, data)			\
> +do {									\
> +	if ((__mask) == 0xffU)						\
> +		writeb((u8)((data) & 0xffU), __addr);			\
> +	else if ((__mask) == 0xff00U)					\
> +		writeb((u8)((data) & 0xffU), (__addr) + 1);		\
> +	else if ((__mask) == 0xff0000U)					\
> +		writeb((u8)((data) & 0xffU), (__addr) + 2);		\
> +	else if ((__mask) == 0xff000000U)				\
> +		writeb((u8)((data) & 0xffU), (__addr) + 3);		\
> +	else if ((__mask) == 0xffffU)					\
> +		writew((u16)((data) & 0xffffU), __addr);		\
> +	else if ((__mask) == 0xffff0000U)				\
> +		writew((u16)((data) & 0xffffU), (__addr) + 2);		\
> +	else if ((__mask) == 0xffffffffU)				\
> +		writel((u32)(data), __addr);				\
> +	else								\
> +		writel((readl(__addr) & ~(__mask)) |			\
> +		       FIELD_PREP(__mask, (u32)(data)), __addr);	\

[Severity: Medium]
Could concurrent updates corrupt the hardware state here? 

This is an unprotected read-modify-write sequence. If a register contains
multiple fields updated from different contexts (e.g., process context
during atomic commits and interrupt context during ISR handling), it
might lose updates without a spinlock.

> +} while (0)

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260809-k3-display-v2-0-327d7910bf71@gmail.com?part=8

-- 
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy

  reply	other threads:[~2026-08-09 13:32 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-09 13:14 [PATCH v2 00/17] drm/spacemit: add SpacemiT K3 display support Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 01/17] dt-bindings: display: spacemit: add K3 Saturn DPU controller Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 02/17] dt-bindings: phy: add SpacemiT K3 Innosilicon DP PHY Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 03/17] dt-bindings: display: spacemit: add K3 Innosilicon DP/eDP controller Cody Kang via B4 Relay
2026-08-09 13:25   ` sashiko-bot
2026-08-09 13:14 ` [PATCH v2 04/17] dt-bindings: soc: spacemit: allow eDP/DP PHY PLL pixel clocks on K3 APMU Cody Kang via B4 Relay
2026-08-09 20:57   ` Rob Herring (Arm)
2026-08-10 14:33   ` Rob Herring (Arm)
2026-08-09 13:14 ` [PATCH v2 05/17] phy: spacemit: add Innosilicon DP TX PHY driver Cody Kang via B4 Relay
2026-08-09 13:26   ` sashiko-bot
2026-08-09 13:14 ` [PATCH v2 06/17] clk: spacemit: k3: parent eDP/DP pixel clock to the PHY PLL Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 07/17] drm/spacemit: add Saturn DPU register model Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 08/17] drm/spacemit: add Saturn DPU core types, cmdlist and display MMU Cody Kang via B4 Relay
2026-08-09 13:32   ` sashiko-bot [this message]
2026-08-09 13:14 ` [PATCH v2 09/17] drm/spacemit: add Saturn DPU hardware backend Cody Kang via B4 Relay
2026-08-09 13:31   ` sashiko-bot
2026-08-09 13:14 ` [PATCH v2 10/17] drm/spacemit: add Saturn DPU KMS pipeline Cody Kang via B4 Relay
2026-08-09 13:35   ` sashiko-bot
2026-08-09 13:14 ` [PATCH v2 11/17] drm/spacemit: add Saturn DPU DRM device driver Cody Kang via B4 Relay
2026-08-09 13:35   ` sashiko-bot
2026-08-09 13:14 ` [PATCH v2 12/17] drm/spacemit: add Innosilicon DP/eDP controller bridge driver Cody Kang via B4 Relay
2026-08-09 13:36   ` sashiko-bot
2026-08-09 13:14 ` [PATCH v2 13/17] MAINTAINERS: add SpacemiT K3 display driver entry Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 14/17] riscv: dts: spacemit: k3: add display nodes Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 15/17] riscv: dts: spacemit: k3-pico-itx: enable the DisplayPort output Cody Kang via B4 Relay
2026-08-09 13:30   ` sashiko-bot
2026-08-09 13:14 ` [PATCH v2 16/17] riscv: dts: spacemit: k3-com260-ifx: " Cody Kang via B4 Relay
2026-08-09 13:14 ` [PATCH v2 17/17] riscv: defconfig: spacemit: k3: enable display driver Cody Kang via B4 Relay

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=20260809133234.F325B1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=codykang.hk@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=linux-phy@lists.infradead.org \
    --cc=neil.armstrong@linaro.org \
    --cc=olteanv@gmail.com \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=vkoul@kernel.org \
    /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