bcm2835_dma_probe() registers the controller with of_dma_controller_register(), but of_dma_controller_free() is never called, neither when dma_async_device_register() fails nor in bcm2835_dma_remove(). The entry stays on the global of_dma_list and keeps a pointer to the driver data, which is devm-allocated and freed when the device is unbound. The next DMA channel request for this node is passed to bcm2835_dma_xlate() with the freed structure and, depending on what that memory holds by then, either fails or crashes. As the stale entry is found first, this still happens after the controller has been bound again. On a Raspberry Pi 4 Model B, unbinding vc4 and the DMA controller and then binding vc4 again results in: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 Call trace: find_candidate+0x64/0x210 (P) dma_get_any_slave_channel+0x64/0xd0 bcm2835_dma_xlate+0x24/0x48 of_dma_request_slave_channel+0x178/0x2a8 dma_request_chan+0x20c/0x380 snd_dmaengine_pcm_register+0xa4/0x2a0 [snd_soc_core] devm_snd_dmaengine_pcm_register+0x60/0xc0 [snd_soc_core] vc4_hdmi_bind+0x4d4/0x8e8 [vc4] Free the controller on the probe error path and in remove. In remove, do it before unregistering the DMA device, so that no channel can be requested through the node while the device is going away. The bug was found and the fix drafted with the help of an AI coding assistant (Claude). Fixes: 96286b576690 ("dmaengine: Add support for BCM2835") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-fable-5-2 Signed-off-by: Tomasz Unger --- Verified on a Raspberry Pi 4 Model B: without the patch the sequence described in the commit message oopses; with it, binding vc4 while the controller is unbound is deferred and HDMI audio gets its DMA channels back once the controller is bound again. Both paths (remove and a forced dma_async_device_register() failure) were also run under KASAN in QEMU raspi4b: slab-use-after-free in find_candidate() without the patch, no report with it. --- drivers/dma/bcm2835-dma.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/dma/bcm2835-dma.c b/drivers/dma/bcm2835-dma.c index 06d830d36882..cf29d897450e 100644 --- a/drivers/dma/bcm2835-dma.c +++ b/drivers/dma/bcm2835-dma.c @@ -1011,13 +1011,15 @@ static int bcm2835_dma_probe(struct platform_device *pdev) if (rc) { dev_err(&pdev->dev, "Failed to register slave DMA engine device: %d\n", rc); - goto err_no_dma; + goto err_of_dma; } dev_dbg(&pdev->dev, "Load BCM2835 DMA engine driver\n"); return 0; +err_of_dma: + of_dma_controller_free(pdev->dev.of_node); err_no_dma: bcm2835_dma_free(od); return rc; @@ -1027,6 +1029,7 @@ static void bcm2835_dma_remove(struct platform_device *pdev) { struct bcm2835_dmadev *od = platform_get_drvdata(pdev); + of_dma_controller_free(pdev->dev.of_node); dma_async_device_unregister(&od->ddev); bcm2835_dma_free(od); } --- base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4 change-id: 20261115-bcm2835-dma-of-dma-free-ef85776c0914 Best regards, -- Tomasz Unger