[PATCH] dmaengine: mmp_pdma: Fix race condition in mmp_pdma_residue()

Guodong Xu posted 1 patch 1 day, 23 hours ago
drivers/dma/mmp_pdma.c | 6 ++++++
1 file changed, 6 insertions(+)
[PATCH] dmaengine: mmp_pdma: Fix race condition in mmp_pdma_residue()
Posted by Guodong Xu 1 day, 23 hours ago
Add proper locking in mmp_pdma_residue() to prevent use-after-free when
accessing descriptor list and descriptor contents.

The race occurs when multiple threads call tx_status() while the tasklet
on another CPU is freeing completed descriptors:

CPU 0                              CPU 1
-----                              -----
mmp_pdma_tx_status()
mmp_pdma_residue()
  -> NO LOCK held
     list_for_each_entry(sw, ..)
                                   DMA interrupt
                                   dma_do_tasklet()
                                     -> spin_lock(&desc_lock)
                                        list_move(sw->node, ...)
                                        spin_unlock(&desc_lock)
  |                                     dma_pool_free(sw) <- FREED!
  -> access sw->desc <- UAF!

This issue can be reproduced when running dmatest on the same channel with
multiple threads (threads_per_chan > 1).

Fix by protecting the chain_running list iteration and descriptor access
with the chan->desc_lock spinlock.

Signed-off-by: Juan Li <lijuan@linux.spacemit.com>
Signed-off-by: Guodong Xu <guodong@riscstar.com>
---
 drivers/dma/mmp_pdma.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c
index d07229a748868b8115892c63c54c16130d88e326..481b58c414e470cc08812d5a9fe7283cc48e5827 100644
--- a/drivers/dma/mmp_pdma.c
+++ b/drivers/dma/mmp_pdma.c
@@ -928,6 +928,7 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan,
 {
 	struct mmp_pdma_desc_sw *sw;
 	struct mmp_pdma_device *pdev = to_mmp_pdma_dev(chan->chan.device);
+	unsigned long flags;
 	u64 curr;
 	u32 residue = 0;
 	bool passed = false;
@@ -945,6 +946,8 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan,
 	else
 		curr = pdev->ops->read_src_addr(chan->phy);
 
+	spin_lock_irqsave(&chan->desc_lock, flags);
+
 	list_for_each_entry(sw, &chan->chain_running, node) {
 		u64 start, end;
 		u32 len;
@@ -989,6 +992,7 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan,
 			continue;
 
 		if (sw->async_tx.cookie == cookie) {
+			spin_unlock_irqrestore(&chan->desc_lock, flags);
 			return residue;
 		} else {
 			residue = 0;
@@ -996,6 +1000,8 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan,
 		}
 	}
 
+	spin_unlock_irqrestore(&chan->desc_lock, flags);
+
 	/* We should only get here in case of cyclic transactions */
 	return residue;
 }

---
base-commit: 8f0b4cce4481fb22653697cced8d0d04027cb1e8
change-id: 20251216-mmp-pdma-race-d77c84219b2c

Best regards,
-- 
Guodong Xu <guodong@riscstar.com>
Re: [PATCH] dmaengine: mmp_pdma: Fix race condition in mmp_pdma_residue()
Posted by Vinod Koul 1 day, 20 hours ago
On Tue, 16 Dec 2025 22:10:06 +0800, Guodong Xu wrote:
> Add proper locking in mmp_pdma_residue() to prevent use-after-free when
> accessing descriptor list and descriptor contents.
> 
> The race occurs when multiple threads call tx_status() while the tasklet
> on another CPU is freeing completed descriptors:
> 
> CPU 0                              CPU 1
> -----                              -----
> mmp_pdma_tx_status()
> mmp_pdma_residue()
>   -> NO LOCK held
>      list_for_each_entry(sw, ..)
>                                    DMA interrupt
>                                    dma_do_tasklet()
>                                      -> spin_lock(&desc_lock)
>                                         list_move(sw->node, ...)
>                                         spin_unlock(&desc_lock)
>   |                                     dma_pool_free(sw) <- FREED!
>   -> access sw->desc <- UAF!
> 
> [...]

Applied, thanks!

[1/1] dmaengine: mmp_pdma: Fix race condition in mmp_pdma_residue()
      commit: a143545855bc2c6e1330f6f57ae375ac44af00a7

Best regards,
-- 
~Vinod