Re: [PATCH] dmaengine: Convert to DEFINE_SIMPLE_DEV_PM_OPS()

[email protected]
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 tasklet teardown on probe error path leads to Use-After-Free.
- [High] Tasklet scheduled before initialization causes NULL pointer dereference, and missing teardown causes Use-After-Free.
- [High] Clock disabled immediately after relaxed writes can lead to lost writes or bus stalls.
--

--- Patch [1]: [PATCH] dmaengine: Convert to DEFINE_SIMPLE_DEV_PM_OPS() ---
commit 362f0670618631761830584ae4ebaa28847cc39c
Author: Triet Hoang <[email protected]>

dmaengine: Convert to DEFINE_SIMPLE_DEV_PM_OPS()

This commit converts the deprecated SIMPLE_DEV_PM_OPS to
DEFINE_SIMPLE_DEV_PM_OPS and pm_sleep_ptr(). It drops the __maybe_unused
annotations from the suspend and resume callbacks, reducing kernel size
when PM configurations are disabled.

> diff --git a/drivers/dma/k3dma.c b/drivers/dma/k3dma.c
> index e84f197fea76f..e0138f5bfd65f 100644
> --- a/drivers/dma/k3dma.c
> +++ b/drivers/dma/k3dma.c
[ ... ]
> -#ifdef CONFIG_PM_SLEEP
>  static int k3_dma_suspend_dev(struct device *dev)
>  {
>  	struct k3_dma_dev *d = dev_get_drvdata(dev);

[Severity: High]
This is a pre-existing issue, but I noticed a potential problem with how
the clock is disabled in k3_dma_suspend_dev().

The suspend function calls k3_dma_enable_dma(d, false), which uses
writel_relaxed() to mask interrupts. Since writel_relaxed() performs posted
writes, they are not guaranteed to reach the peripheral immediately.

Right after this, the clock is disabled via clk_disable_unprepare(d->clk).

Can disabling the clock while a bus write is still in-flight cause the write
to be lost (leaving interrupts unmasked) or trigger a system-wide bus stall?
Should there be a dummy readback to flush the bus before the clock is
disabled?

[ ... ]
>  static struct platform_driver k3_pdma_driver = {
>  	.driver		= {
>  		.name	= DRIVER_NAME,
> -		.pm	= &k3_dma_pmops,
> +		.pm	= pm_sleep_ptr(&k3_dma_pmops),
>  		.of_match_table = k3_pdma_dt_ids,
>  	},
>  	.probe		= k3_dma_probe,

[Severity: High]
This is a pre-existing issue, but I noticed a potential initialization and
teardown issue in k3_dma_probe().

The probe function requests an IRQ and unmasks interrupts via
k3_dma_enable_dma(d, true) before the tasklet is initialized:

    k3_dma_enable_dma(d, true);

    ret = dma_async_device_register(&d->slave);
    if (ret)
        goto dma_async_register_fail;
    ...
    tasklet_setup(&d->task, k3_dma_tasklet);

If an interrupt fires immediately, k3_dma_int_handler() could call
tasklet_schedule(&d->task). Since d->task is zero-allocated and hasn't
been initialized by tasklet_setup() yet, wouldn't this cause a NULL
pointer dereference?

Additionally, if the probe fails later (for example, in
dma_async_device_register()), the error path relies on devres to free
memory but omits tasklet_kill(). Does this leave pending virtual channel
tasklets pointing to freed memory, causing a use-after-free?

> diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c
> index bf805f1024f64..e6d7c72fce3ce 100644
> --- a/drivers/dma/pch_dma.c
> +++ b/drivers/dma/pch_dma.c
[ ... ]
>  static struct pci_driver pch_dma_driver = {
>  	.name		= DRV_NAME,
>  	.id_table	= pch_dma_id_table,
>  	.probe		= pch_dma_probe,

[Severity: High]
This is a pre-existing issue, but looking at pch_dma_probe(), there appears
to be a missing tasklet teardown on the error path.

The probe function requests an IRQ with IRQF_SHARED. If a shared interrupt
fires, pd_irq() executes, reads the device status, and might schedule
pd_chan->tasklet if the hardware status bits are non-zero.

If the probe subsequently fails (e.g., in dma_async_device_register()),
the error path calls free_irq() and frees the memory, but omits calling
tasklet_kill(&pd_chan->tasklet):

err_free_pool:
        dma_pool_destroy(pd->pool);
err_free_irq:
        free_irq(pdev->irq, pd);
err_iounmap:

Could the tasklet remain queued and execute after pd is freed, resulting in
a use-after-free?

>  	.remove		= pch_dma_remove,
> -	.driver.pm	= &pch_dma_pm_ops,
> +	.driver.pm	= pm_sleep_ptr(&pch_dma_pm_ops),
>  };

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.