[PATCH] mmc: bcm2835: guard against host->data being NULL in bcm2835_finish_data()

Matt Brocklehurst <[email protected]> Sat, 8 Aug 2026 14:12:27 +0100
Newsgroups org.kernel.vger.linux-mmc
Message-ID <[email protected]>
bcm2835_dma_complete_work() calls bcm2835_finish_data() unconditionally
from a workqueue once the DMA engine signals completion. But the same
transfer can also complete via the interrupt/status-poll path first
(bcm2835_finish_data() -> bcm2835_transfer_complete(), which sets
host->data = NULL) before that deferred DMA-completion callback gets
to run. When the DMA-completion path loses that race, host->data is
already NULL and the next line dereferences it unconditionally.

Hit this as a real NULL pointer dereference while bringing up BCM2836/
BCM2837 support in a downstream build system, confirmed live via a
kernel panic on the serial console:

  Unable to handle kernel NULL pointer dereference at virtual address 00000014
  PC is at bcm2835_finish_data+0x28/0x60
  ...
  bcm2835_finish_data from bcm2835_dma_complete_work+0xf8/0x124

This code path is only reachable once a DMA transfer actually
completes successfully, which is presumably why it hasn't surfaced
before on setups where DMA transfers complete quickly and reliably --
the race window is narrow. It is not specific to any particular SoC
or devicetree configuration; both bcm2835_finish_data() and
bcm2835_dma_complete_work() are unchanged from mainline, so any board
using this driver can hit it.

Signed-off-by: Matt Brocklehurst <[email protected]>
---
 drivers/mmc/host/bcm2835.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c
index ee63835..fbcdfac 100644
--- a/drivers/mmc/host/bcm2835.c
+++ b/drivers/mmc/host/bcm2835.c
@@ -717,6 +717,17 @@ static void bcm2835_finish_data(struct bcm2835_host *host)
 
 	data = host->data;
 
+	/*
+	 * host->data can already be NULL here: bcm2835_dma_complete_work()
+	 * calls this unconditionally from a workqueue, but the same
+	 * transfer may already have completed via the interrupt/status-poll
+	 * path (bcm2835_finish_data() -> bcm2835_transfer_complete(), which
+	 * sets host->data = NULL) before the deferred DMA-completion
+	 * callback runs.
+	 */
+	if (!data)
+		return;
+
 	host->hcfg &= ~(SDHCFG_DATA_IRPT_EN | SDHCFG_BLOCK_IRPT_EN);
 	writel(host->hcfg, host->ioaddr + SDHCFG);
 
-- 
2.39.5 (Apple Git-154)