Re: [PATCH v2 11/64] dmaengine: mmp_tdma: hook completions to dma_chan BH

[email protected] Mon, 27 Jul 2026 20:55:32 +0000
Newsgroups org.kernel.vger.dmaengine
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Missing teardown of bottom-half work during channel release, leading to potential use-after-free.
- [High] Incorrect devres initialization ordering for shared interrupts causes NULL pointer dereferences on probe and use-after-free on unbind.
- [High] SRAM descriptors allocated from the `gen_pool` are leaked when `mmp_tdma_prep_dma_cyclic` fails or is called multiple times.
--

--- Patch [11]: [PATCH v2 11/64] dmaengine: mmp_tdma: hook completions to dma_chan BH ---
commit c186dbcb9d7ae1ce6b3473be898cbd442e74d154
Author: Allen Pais <[email protected]>

dmaengine: mmp_tdma: hook completions to dma_chan BH

This commit replaces the per-channel tasklet with the shared dma_chan BH helper.
The handler continues to run in softirq context while dmaengine manages the
common scheduling and teardown mechanism.

> diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c
> index ba03321eeff79..d1ed2886f0e70 100644
> --- a/drivers/dma/mmp_tdma.c
> +++ b/drivers/dma/mmp_tdma.c

[ ... ]

> @@ -320,7 +319,7 @@ static irqreturn_t mmp_tdma_chan_handler(int irq, void *dev_id)
>  	struct mmp_tdma_chan *tdmac = dev_id;
>  
>  	if (mmp_tdma_clear_chan_irq(tdmac) == 0) {
> -		tasklet_schedule(&tdmac->tasklet);
> +		dma_chan_schedule_bh(&tdmac->chan);
>  		return IRQ_HANDLED;
>  	} else
>  		return IRQ_NONE;
> @@ -346,9 +345,9 @@ static irqreturn_t mmp_tdma_int_handler(int irq, void *dev_id)
>  		return IRQ_NONE;
>  }

[Severity: High]
This is a pre-existing issue, but is it possible for this interrupt handler to
trigger before the channel structures are fully initialized?

Looking at mmp_tdma_probe(), devm_request_irq() is called with IRQF_SHARED
before the channel structures are allocated and assigned:

drivers/dma/mmp_tdma.c:mmp_tdma_probe() {
    ...
    ret = devm_request_irq(&pdev->dev, irq,
                           mmp_tdma_int_handler, IRQF_SHARED, "tdma", tdev);
    ...
}

If the shared IRQ line fires immediately, mmp_tdma_int_handler() iterates over
tdev->tdmac[] and unconditionally passes a potentially uninitialized NULL pointer
to mmp_tdma_chan_handler(). Could this lead to a NULL pointer dereference on
probe, or a use-after-free on unbind since devres reverses the initialization
order?

> -static void dma_do_tasklet(struct tasklet_struct *t)
> +static void dma_do_tasklet(struct dma_chan *c)
>  {
> -	struct mmp_tdma_chan *tdmac = from_tasklet(tdmac, t, tasklet);
> +	struct mmp_tdma_chan *tdmac = container_of(c, struct mmp_tdma_chan, chan);
>  
>  	dmaengine_desc_get_callback_invoke(&tdmac->desc, NULL);
>  }
> @@ -583,7 +582,7 @@ static int mmp_tdma_chan_init(struct mmp_tdma_device *tdev,
>  	tdmac->pool	   = pool;
>  	tdmac->status = DMA_COMPLETE;
>  	tdev->tdmac[tdmac->idx] = tdmac;
> -	tasklet_setup(&tdmac->tasklet, dma_do_tasklet);
> +	dma_chan_init_bh(&tdmac->chan, dma_do_tasklet);

[Severity: High]
This isn't a bug introduced by this patch, but should the bottom-half work be
explicitly torn down during channel release?

Currently, mmp_tdma_free_chan_resources() frees channel resources without
calling dma_chan_kill_bh():

drivers/dma/mmp_tdma.c:mmp_tdma_free_chan_resources() {
    ...
}

Similarly, when the device unbinds, devres frees the channel structure (tdmac)
but mmp_tdma_remove() does not explicitly kill the bottom-half work.

Could this leave pending bottom-half tasks to execute after memory is freed by
devres, leading to a use-after-free?

>  	/* add the channel to tdma_chan list */
>  	list_add_tail(&tdmac->chan.device_node,

[Severity: High]
This is a pre-existing issue, but can mmp_tdma_prep_dma_cyclic() leak SRAM
descriptors allocated from the gen_pool?

If mmp_tdma_config_write() fails, the function branches to err_out without
freeing the newly allocated descriptor:

drivers/dma/mmp_tdma.c:mmp_tdma_prep_dma_cyclic() {
    ...
    desc = mmp_tdma_alloc_descriptor(tdmac);
    if (!desc)
        goto err_out;

    if (mmp_tdma_config_write(chan, direction, &tdmac->slave_config))
        goto err_out;
    ...
}

Additionally, mmp_tdma_alloc_descriptor() unconditionally assigns the newly
allocated memory to tdmac->desc_arr:

drivers/dma/mmp_tdma.c:mmp_tdma_alloc_descriptor() {
    ...
    tdmac->desc_arr = gen_pool_dma_alloc(gpool, size, &tdmac->desc_arr_phys);
    ...
}

If mmp_tdma_prep_dma_cyclic() is called multiple times without an intervening
mmp_tdma_free_chan_resources(), the previous descriptor is blindly overwritten
and permanently leaked. Could this exhaust the limited device SRAM pool and
prevent further DMA operations?

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=11