DMA Engine development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Hongling Zeng" <zenghongling@kylinos.cn>
Cc: Frank.Li@kernel.org, vkoul@kernel.org, dmaengine@vger.kernel.org,
	linux-sunxi@lists.linux.dev
Subject: Re: [PATCH v5] dmaengine: sun6i-dma: Fix use-after-free in error handling paths
Date: Thu, 09 Jul 2026 01:43:24 +0000	[thread overview]
Message-ID: <20260709014325.40ACB1F00A3E@smtp.kernel.org> (raw)
In-Reply-To: <20260709013118.616467-1-zenghongling@kylinos.cn>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Deadlock in sun6i_dma_terminate_all and sun6i_dma_pause due to unsafe lock context.
- [High] Race condition in sun6i_dma_terminate_all, sun6i_dma_pause, and sun6i_dma_resume due to lockless read of vchan->phy, causing memory corruption and state inconsistency.
- [High] Race condition causing a NULL pointer dereference in sun6i_dma_interrupt.
--

--- Patch [1]: [PATCH v5] dmaengine: sun6i-dma: Fix use-after-free in error handling paths ---
commit 4eb50f2f9528d3f1d66f48feef49a56e2cf57490
Author: Hongling Zeng <zenghongling@kylinos.cn>

dmaengine: sun6i-dma: Fix use-after-free in error handling paths

In error handling paths, the for loop frees v_lli in the loop body,
then accesses v_lli->v_lli_next and v_lli->p_lli_next in the
increment expression, which is use-after-free.

Fix by refactoring the cleanup into a helper function sun6i_dma_free_desc()
that saves both the next virtual and physical pointers before freeing the
current node, preventing the use-after-free.

> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index a9a254dbf8cb9..7a79f346250a5 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -406,16 +406,12 @@ static inline void sun6i_dma_dump_lli(struct sun6i_vchan *vchan,
>  		v_lli->len, v_lli->para, v_lli->p_lli_next);
>  }
>  
> -static void sun6i_dma_free_desc(struct virt_dma_desc *vd)
> +static void sun6i_dma_free_desc(struct sun6i_dma_dev *sdev,
> +				struct sun6i_desc *txd)
>  {

[ ... ]

[Severity: High]
This is a pre-existing issue, but can this cause a deadlock?

In sun6i_dma_terminate_all() and sun6i_dma_pause(), we acquire the lock
without disabling softirqs:

drivers/dma/sun6i-dma.c:sun6i_dma_terminate_all() {
    ...
	spin_lock(&sdev->lock);
	list_del_init(&vchan->node);
	spin_unlock(&sdev->lock);
    ...
}

If a hardware interrupt for the DMA controller fires on the same CPU while
this lock is held, the hardirq handler will execute and schedule
sun6i_dma_tasklet. Upon return from the hardware interrupt, softirqs are
processed immediately on the same CPU, forcing the tasklet to execute.

The tasklet unconditionally attempts to acquire sdev->lock using
spin_lock_irq():

drivers/dma/sun6i-dma.c:sun6i_dma_tasklet() {
    ...
	spin_lock_irq(&sdev->lock);
    ...
}

Because the lock is already held by the interrupted process context on the
same CPU, won't the tasklet spin forever and deadlock the CPU? Should these
use spin_lock_bh() or spin_lock_irqsave()?

[Severity: High]
This isn't a bug introduced by this patch, but does this lockless read of
vchan->phy create a race condition?

drivers/dma/sun6i-dma.c:sun6i_dma_terminate_all() {
	struct sun6i_pchan *pchan = vchan->phy;
    ...
	spin_lock(&sdev->lock);
	list_del_init(&vchan->node);
	spin_unlock(&sdev->lock);

	spin_lock_irqsave(&vchan->vc.lock, flags);
    ...
	if (pchan) {
		writel(DMA_CHAN_ENABLE_STOP, pchan->base + DMA_CHAN_ENABLE);
		writel(DMA_CHAN_PAUSE_RESUME, pchan->base + DMA_CHAN_PAUSE);
		vchan->phy = NULL;
	}
    ...
}

pchan = vchan->phy is read at the very beginning of the function, outside
of any locks.

Concurrently, sun6i_dma_tasklet() can acquire sdev->lock, assign a physical
channel to vchan->phy, and start the hardware transfer. If this race occurs,
sun6i_dma_terminate_all() sees pchan as NULL.

It subsequently acquires the necessary locks but skips the if (pchan) block,
failing to write DMA_CHAN_ENABLE_STOP to the hardware.

Will this function then return success, deceiving the caller into believing
the channel is stopped and descriptors are freed, while the hardware continues
transferring data into freed memory?

The same lockless read in sun6i_dma_resume() also seems to allow the virtual
channel to be re-added to the pending list while already having an active
physical channel.

[Severity: High]
This is a pre-existing issue, but can this sequence lead to a NULL pointer
dereference?

drivers/dma/sun6i-dma.c:sun6i_dma_interrupt() {
    ...
			if (vchan && (status & vchan->irq_type)) {
				if (vchan->cyclic) {
					vchan_cyclic_callback(&pchan->desc->vd);
				} else {
					spin_lock(&vchan->vc.lock);
					vchan_cookie_complete(&pchan->desc->vd);
    ...
}

In sun6i_dma_interrupt(), if the channel is not cyclic, the code blocks on
spin_lock(&vchan->vc.lock).

Concurrently, a process context thread running sun6i_dma_terminate_all()
can acquire vchan->vc.lock, stop the channel, and set pchan->desc = NULL.

Once sun6i_dma_terminate_all() releases the lock, the interrupt handler
unblocks and immediately calls vchan_cookie_complete(&pchan->desc->vd).

Since pchan->desc has just been nullified, won't dereferencing
pchan->desc->vd cause a kernel oops?

Additionally, if vchan->cyclic is true, the interrupt handler calls
vchan_cyclic_callback(&pchan->desc->vd) without taking any locks, exposing
pchan->desc to concurrent nullification.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260709013118.616467-1-zenghongling@kylinos.cn?part=1

      reply	other threads:[~2026-07-09  1:43 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-09  1:31 [PATCH v5] dmaengine: sun6i-dma: Fix use-after-free in error handling paths Hongling Zeng
2026-07-09  1:43 ` sashiko-bot [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=20260709014325.40ACB1F00A3E@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Frank.Li@kernel.org \
    --cc=dmaengine@vger.kernel.org \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=vkoul@kernel.org \
    --cc=zenghongling@kylinos.cn \
    /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