[PATCH] dmaengine: mmp_pdma: Fix race condition in mmp_pdma_residue()
Guodong Xu
guodong at riscstar.com
Tue Dec 16 06:10:06 PST 2025
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 at linux.spacemit.com>
Signed-off-by: Guodong Xu <guodong at 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 at riscstar.com>
More information about the linux-riscv
mailing list