From b25643e4f3396aa6412e9c3f4e1f40e1f3cfa805 Mon Sep 17 00:00:00 2001 From: Philip Molloy Date: Fri, 6 Feb 2026 11:38:15 +0100 Subject: [PATCH] dmaengine: adi-dma: fix use-after-free in cyclic transfers A race condition exists in the threaded interrupt handler when processing cyclic DMA descriptors. The handler accesses channel->current_desc->result after releasing the channel lock, creating a window where another CPU executing adi_dma_terminate_all() can free the descriptor. Race sequence: CPU 0 (thread handler) CPU 1 (terminate_all) ---------------------- --------------------- Lock channel->lock Check current_desc->cyclic Get callback pointer Unlock channel->lock Lock channel->lock Free current_desc Unlock channel->lock Access current_desc->result <- Use-after-free This results in accessing freed memory containing list poison values (0xdead000000000100), leading to kernel crashes. Fix this by copying the result structure to a local variable while still holding the lock, then using the copy after unlocking. This follows the same safe pattern used in the non-cyclic path, which already uses a local descriptor pointer. The fix is minimal and avoids the complications warned about in the original code comment regarding cyclic descriptors and the pending list during termination. Signed-off-by: Claude Opus 4.6 Signed-off-by: Philip Molloy --- drivers/dma/adi-dma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/adi-dma.c b/drivers/dma/adi-dma.c index b631b5e0aaa2b1..d8153b9c8779bb 100644 --- a/drivers/dma/adi-dma.c +++ b/drivers/dma/adi-dma.c @@ -896,10 +896,12 @@ static irqreturn_t adi_dma_thread_handler(int irq, void *id) spin_lock_irqsave(&channel->lock, flags); if (channel->current_desc && channel->current_desc->cyclic) { + struct dmaengine_result result = channel->current_desc->result; + dmaengine_desc_get_callback(&channel->current_desc->tx, &cb); spin_unlock_irqrestore(&channel->lock, flags); - dmaengine_desc_callback_invoke(&cb, &channel->current_desc->result); + dmaengine_desc_callback_invoke(&cb, &result); return IRQ_HANDLED; }