Skip to content

Commit a143545

Browse files
docularxuvinodkoul
authored andcommitted
dmaengine: mmp_pdma: Fix race condition in mmp_pdma_residue()
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> Link: https://patch.msgid.link/20251216-mmp-pdma-race-v1-1-976a224bb622@riscstar.com Signed-off-by: Vinod Koul <vkoul@kernel.org>
1 parent 430f780 commit a143545

1 file changed

Lines changed: 6 additions & 0 deletions

File tree

drivers/dma/mmp_pdma.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,7 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan,
928928
{
929929
struct mmp_pdma_desc_sw *sw;
930930
struct mmp_pdma_device *pdev = to_mmp_pdma_dev(chan->chan.device);
931+
unsigned long flags;
931932
u64 curr;
932933
u32 residue = 0;
933934
bool passed = false;
@@ -945,6 +946,8 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan,
945946
else
946947
curr = pdev->ops->read_src_addr(chan->phy);
947948

949+
spin_lock_irqsave(&chan->desc_lock, flags);
950+
948951
list_for_each_entry(sw, &chan->chain_running, node) {
949952
u64 start, end;
950953
u32 len;
@@ -989,13 +992,16 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan,
989992
continue;
990993

991994
if (sw->async_tx.cookie == cookie) {
995+
spin_unlock_irqrestore(&chan->desc_lock, flags);
992996
return residue;
993997
} else {
994998
residue = 0;
995999
passed = false;
9961000
}
9971001
}
9981002

1003+
spin_unlock_irqrestore(&chan->desc_lock, flags);
1004+
9991005
/* We should only get here in case of cyclic transactions */
10001006
return residue;
10011007
}

0 commit comments

Comments
 (0)