sf_pdma_issue_pending() sets pointer to the currently being processed dma descriptor in struct sf_pdma_chan, but this descriptor remains a part of the desc_issued list from the struct virt_dma_chan. Descriptor is correctly deleted from the list and freed in done tasklet, but stays in place in case of an error during dma processing, waiting for sf_pdma_terminate_all() to be called. If the pointer to the descriptor is valid in struct sf_pdma_chan, sf_pdma_terminate_all() first frees this descriptor directly, but later uses it again in the vchan_dma_desc_free_list(), leading to mem management errors (double-free, use-after-free). Signed-off-by: Nikita Proshkin <n.proshkin@xxxxxxxxx> --- drivers/dma/sf-pdma/sf-pdma.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c index 55b7c57eeec9..8fec11ad4f0b 100644 --- a/drivers/dma/sf-pdma/sf-pdma.c +++ b/drivers/dma/sf-pdma/sf-pdma.c @@ -145,7 +145,6 @@ static void sf_pdma_free_chan_resources(struct dma_chan *dchan) spin_lock_irqsave(&chan->vchan.lock, flags); sf_pdma_disable_request(chan); - kfree(chan->desc); chan->desc = NULL; vchan_get_all_descriptors(&chan->vchan, &head); sf_pdma_disclaim_chan(chan); @@ -192,7 +191,6 @@ static int sf_pdma_terminate_all(struct dma_chan *dchan) spin_lock_irqsave(&chan->vchan.lock, flags); sf_pdma_disable_request(chan); - kfree(chan->desc); chan->desc = NULL; vchan_get_all_descriptors(&chan->vchan, &head); spin_unlock_irqrestore(&chan->vchan.lock, flags); @@ -279,8 +277,10 @@ static void sf_pdma_donebh_tasklet(struct tasklet_struct *t) spin_lock_irqsave(&chan->vchan.lock, flags); chan->status = DMA_COMPLETE; - list_del(&chan->desc->vdesc.node); - vchan_cookie_complete(&chan->desc->vdesc); + if (chan->desc) { + list_del(&chan->desc->vdesc.node); + vchan_cookie_complete(&chan->desc->vdesc); + } chan->desc = sf_pdma_get_first_pending_desc(chan); if (chan->desc) @@ -295,7 +295,8 @@ static void sf_pdma_errbh_tasklet(struct tasklet_struct *t) unsigned long flags; spin_lock_irqsave(&chan->vchan.lock, flags); - dmaengine_desc_get_callback_invoke(chan->desc->async_tx, NULL); + if (chan->desc) + dmaengine_desc_get_callback_invoke(chan->desc->async_tx, NULL); chan->status = DMA_ERROR; spin_unlock_irqrestore(&chan->vchan.lock, flags); } @@ -310,7 +311,7 @@ static irqreturn_t sf_pdma_done_isr(int irq, void *dev_id) writel((readl(regs->ctrl)) & ~PDMA_DONE_STATUS_MASK, regs->ctrl); residue = readq(regs->residue); - if (!residue) { + if (!residue || !chan->desc) { tasklet_hi_schedule(&chan->done_tasklet); } else { /* submit next trascatioin if possible */ -- 2.34.1