DMA Engine development
 help / color / mirror / Atom feed
* [PATCH] dmaengine: sun6i-dma: Fix memory leak in sun6i_dma_terminate_all
@ 2026-06-16  3:31 Hongling Zeng
  2026-06-16  3:42 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: Hongling Zeng @ 2026-06-16  3:31 UTC (permalink / raw)
  To: vkoul, Frank.Li, wens, jernej.skrabec, samuel, mripard, arnd
  Cc: dmaengine, linux-arm-kernel, linux-sunxi, linux-kernel,
	zhongling0719, Hongling Zeng

When terminating a non-cyclic DMA transfer, the active descriptor
is not properly reclaimed. The descriptor is removed from the
desc_issued list in sun6i_dma_start_desc(), but in
sun6i_dma_terminate_all(), only cyclic transfer descriptors are
added to the desc_completed list before cleanup.

For non-cyclic transfers, pchan->desc is set to NULL without first
adding the descriptor back to a list that vchan_get_all_descriptors()
can collect. This causes the descriptor and its associated LLI chain
to be permanently leaked.

Fix by ensuring both cyclic and non-cyclic active descriptors are
added to the desc_completed list before setting pchan->desc to NULL.

Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller")
Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn>
---
 drivers/dma/sun6i-dma.c | 12 +++++-------
 1 file changed, 5 insertions(+), 7 deletions(-)

diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
index 7a79f346250a..97730ba6c874 100644
--- a/drivers/dma/sun6i-dma.c
+++ b/drivers/dma/sun6i-dma.c
@@ -946,16 +946,14 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
 
 	spin_lock_irqsave(&vchan->vc.lock, flags);
 
-	if (vchan->cyclic) {
-		vchan->cyclic = false;
-		if (pchan && pchan->desc) {
-			struct virt_dma_desc *vd = &pchan->desc->vd;
-			struct virt_dma_chan *vc = &vchan->vc;
+	if (pchan && pchan->desc) {
+		struct virt_dma_desc *vd = &pchan->desc->vd;
+		struct virt_dma_chan *vc = &vchan->vc;
 
-			list_add_tail(&vd->node, &vc->desc_completed);
-		}
+		list_add_tail(&vd->node, &vc->desc_completed);
 	}
 
+	vchan->cyclic = false;
 	vchan_get_all_descriptors(&vchan->vc, &head);
 
 	if (pchan) {
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH] dmaengine: sun6i-dma: Fix memory leak in sun6i_dma_terminate_all
  2026-06-16  3:31 [PATCH] dmaengine: sun6i-dma: Fix memory leak in sun6i_dma_terminate_all Hongling Zeng
@ 2026-06-16  3:42 ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-06-16  3:42 UTC (permalink / raw)
  To: Hongling Zeng; +Cc: dmaengine, Frank.Li, vkoul, linux-sunxi

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

New issues:
- [High] The patch unconditionally adds the active descriptor `pchan->desc` to `vc->desc_completed` in `sun6i_dma_terminate_all()`, causing list corruption and a potential use-after-free for non-cyclic transfers.

Pre-existing issues:
- [High] A TOCTOU race condition between `sun6i_dma_interrupt()` and `sun6i_dma_terminate_all()` allows the interrupt handler to dereference a NULL `pchan->desc` pointer.
- [High] Use-after-free when calculating residue for a recently completed transfer in `sun6i_dma_tx_status()`.
- [High] Deadlock risk due to acquiring `sdev->lock` with plain `spin_lock()` in process context, which is vulnerable to softirq preemption.
- [High] The interrupt handler locklessly reads `pchan->vchan`, which races with channel reallocation and can prematurely complete a newly started transfer.
--

commit c1c2a26ec23500f38a915dc234fa3b40c6cbad7d
Author: Hongling Zeng <zenghongling@kylinos.cn>

dmaengine: sun6i-dma: Fix memory leak in sun6i_dma_terminate_all

This commit fixes a memory leak in sun6i_dma_terminate_all() for non-cyclic
DMA transfers. It ensures that both cyclic and non-cyclic active descriptors
are added to the desc_completed list before setting pchan->desc to NULL.

> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index a9a254dbf8cb9..7f9414e3a3814 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c
> @@ -945,16 +945,14 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
>  
>  	spin_lock_irqsave(&vchan->vc.lock, flags);

[Severity: High]
This isn't a bug introduced by this patch, but just above this line in
sun6i_dma_terminate_all(), there is a plain spin_lock() used in process
context:

	spin_lock(&sdev->lock);
	list_del_init(&vchan->node);
	spin_unlock(&sdev->lock);

Does this risk an infinite spinlock deadlock? Since sun6i_dma_terminate_all()
is callable from process context, a hardirq can preempt execution while
sdev->lock is held. When the hardirq finishes, the pending softirqs would run
sun6i_dma_tasklet on the same CPU, which attempts to acquire the lock using
spin_lock_irqsave(&sdev->lock, flags), potentially deadlocking.

> -	if (vchan->cyclic) {
> -		vchan->cyclic = false;
> -		if (pchan && pchan->desc) {
> -			struct virt_dma_desc *vd = &pchan->desc->vd;
> -			struct virt_dma_chan *vc = &vchan->vc;
> +	if (pchan && pchan->desc) {
> +		struct virt_dma_desc *vd = &pchan->desc->vd;
> +		struct virt_dma_chan *vc = &vchan->vc;
>  
> -			list_add_tail(&vd->node, &vc->desc_completed);
> -		}
> +		list_add_tail(&vd->node, &vc->desc_completed);

[Severity: High]
Does unconditionally adding the descriptor to desc_completed risk list
corruption for non-cyclic transfers?

When a non-cyclic transfer completes, sun6i_dma_interrupt() calls
vchan_cookie_complete(), which places the descriptor on vc->desc_completed,
sets pchan->done = pchan->desc, and schedules the vchan_complete tasklet.
However, pchan->desc isn't cleared until sun6i_dma_tasklet runs.

If sun6i_dma_terminate_all() executes during this window, it observes a
non-NULL pchan->desc and executes list_add_tail() again. Since the descriptor
is already on the list (or has been spliced/freed by the completion tasklet),
this double-add could corrupt the linked list pointers. Should this check
if (pchan->desc != pchan->done) first?

[ ... ]

[Severity: High]
This is a pre-existing issue, but is there a TOCTOU race condition between
sun6i_dma_interrupt() and sun6i_dma_terminate_all()?

In sun6i_dma_interrupt(), pchan->vchan is read locklessly. If
sun6i_dma_terminate_all() runs concurrently on another CPU, it can acquire the
channel lock, set pchan->desc = NULL, and release the lock:

CPU1 (sun6i_dma_terminate_all):
    if (pchan) {
        ...
        pchan->desc = NULL;
    }

CPU2 (sun6i_dma_interrupt):
    spin_lock(&vchan->vc.lock);
    vchan_cookie_complete(&pchan->desc->vd);

Could this cause a fatal NULL pointer dereference when &pchan->desc->vd
evaluates to NULL inside vchan_cookie_complete()?


[Severity: High]
This is a pre-existing issue, but does sun6i_dma_tx_status() have a
use-after-free risk when calculating residue?

When a transfer finishes, the vchan_complete tasklet completes the callback
and frees the descriptor memory. However, pchan->desc is not cleared until
the separate sun6i_dma_tasklet runs.

If dmaengine_tx_status() is called in this window, vchan_find_desc() returns
NULL, and the code falls back to checking pchan && pchan->desc. It then calls
sun6i_get_chan_size(pchan), which accesses the descriptor:

drivers/dma/sun6i-dma.c:sun6i_get_chan_size() {
    ...
    for (lli = txd->v_lli; lli; lli = lli->v_lli_next) {
    ...
}

Will this dereference the already-freed descriptor memory?


[Severity: High]
This is a pre-existing issue, but does the interrupt handler risk prematurely
completing a newly started transfer?

sun6i_dma_terminate_all() stops the physical channel but leaves pending
interrupt bits set in the hardware DMA_IRQ_STAT register. The
sun6i_dma_tasklet can immediately reallocate this physical channel to a
new virtual channel and start it.

If the delayed hardware interrupt from the old transfer fires,
sun6i_dma_interrupt() reads pchan->vchan locklessly:

drivers/dma/sun6i-dma.c:sun6i_dma_interrupt() {
    ...
    vchan = pchan->vchan;
    if (vchan && (status & vchan->irq_type)) {
    ...
}

If it evaluates as the newly assigned vchan, and non-cyclic transfers share
the same irq_type bitmask, could the handler incorrectly acquire the new
vchan->vc.lock and prematurely complete the new transfer's descriptor?

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

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-06-16  3:42 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-16  3:31 [PATCH] dmaengine: sun6i-dma: Fix memory leak in sun6i_dma_terminate_all Hongling Zeng
2026-06-16  3:42 ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox