From 966d23c7e68ea32679275a7e3d2383181002c868 Mon Sep 17 00:00:00 2001 From: Mukesh Ojha Date: Fri, 24 Jul 2026 15:19:37 +0530 Subject: [PATCH] firmware: qcom: scm: Fix NULL dereference in IRQ handler before __scm is published In qcom_scm_probe(), devm_request_threaded_irq() is called before smp_store_release(&__scm, scm). Two paths can dereference __scm before it is published, both causing a NULL pointer dereference. The IRQ handler receives scm via its data argument but passes only wq_ctx to qcom_scm_waitq_wakeup() and qcom_scm_get_completion(), which then dereference __scm directly. Thread scm through both functions so the IRQ handler path never touches __scm. Non-atomic SMC calls made during probe (e.g. from qcom_tzmem_init via qcom_scm_shm_bridge_enable) can return WAITQ_SLEEP, causing qcom_scm_wait_for_wq_completion() to run before __scm is published and dereference it. Add platform_set_drvdata(pdev, scm) early in probe and change qcom_scm_wait_for_wq_completion() to take the device pointer and use dev_get_drvdata() to reach scm, removing any dependency on __scm. Fixes: 6bf325992236 ("firmware: qcom: scm: Add wait-queue handling logic") Reviewed-by: Bartosz Golaszewski Reviewed-by: Konrad Dybcio Signed-off-by: Mukesh Ojha Link: https://lore.kernel.org/r/20260724094939.613844-2-mukesh.ojha@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom/qcom_scm-smc.c | 2 +- drivers/firmware/qcom/qcom_scm.c | 22 ++++++++++------------ drivers/firmware/qcom/qcom_scm.h | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/firmware/qcom/qcom_scm-smc.c b/drivers/firmware/qcom/qcom_scm-smc.c index 01999c22659c..127365ab11fc 100644 --- a/drivers/firmware/qcom/qcom_scm-smc.c +++ b/drivers/firmware/qcom/qcom_scm-smc.c @@ -111,7 +111,7 @@ static int __scm_smc_do_quirk_handle_waitq(struct device *dev, struct arm_smccc_ smc_call_ctx = res->a2; trace_scm_waitq_sleep(wq_ctx, smc_call_ctx); - ret = qcom_scm_wait_for_wq_completion(wq_ctx); + ret = qcom_scm_wait_for_wq_completion(dev, wq_ctx); if (ret) return ret; diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c index 9ec79b445481..d11026c26cde 100644 --- a/drivers/firmware/qcom/qcom_scm.c +++ b/drivers/firmware/qcom/qcom_scm.c @@ -2645,23 +2645,20 @@ static int qcom_scm_get_waitq_irq(struct qcom_scm *scm) return irq_create_fwspec_mapping(&fwspec); } -static struct completion *qcom_scm_get_completion(u32 wq_ctx) +static struct completion *qcom_scm_get_completion(struct qcom_scm *scm, u32 wq_ctx) { - struct completion *wq; - - if (WARN_ON_ONCE(wq_ctx >= __scm->wq_cnt)) + if (WARN_ON_ONCE(wq_ctx >= scm->wq_cnt)) return ERR_PTR(-EINVAL); - wq = &__scm->waitq_comps[wq_ctx]; - - return wq; + return &scm->waitq_comps[wq_ctx]; } -int qcom_scm_wait_for_wq_completion(u32 wq_ctx) +int qcom_scm_wait_for_wq_completion(struct device *dev, u32 wq_ctx) { + struct qcom_scm *scm = dev_get_drvdata(dev); struct completion *wq; - wq = qcom_scm_get_completion(wq_ctx); + wq = qcom_scm_get_completion(scm, wq_ctx); if (IS_ERR(wq)) return PTR_ERR(wq); @@ -2670,11 +2667,11 @@ int qcom_scm_wait_for_wq_completion(u32 wq_ctx) return 0; } -static int qcom_scm_waitq_wakeup(unsigned int wq_ctx) +static int qcom_scm_waitq_wakeup(struct qcom_scm *scm, unsigned int wq_ctx) { struct completion *wq; - wq = qcom_scm_get_completion(wq_ctx); + wq = qcom_scm_get_completion(scm, wq_ctx); if (IS_ERR(wq)) return PTR_ERR(wq); @@ -2701,7 +2698,7 @@ static irqreturn_t qcom_scm_irq_handler(int irq, void *data) goto out; } - ret = qcom_scm_waitq_wakeup(wq_ctx); + ret = qcom_scm_waitq_wakeup(scm, wq_ctx); if (ret) goto out; } while (more_pending); @@ -2805,6 +2802,7 @@ static int qcom_scm_probe(struct platform_device *pdev) return -ENOMEM; scm->dev = &pdev->dev; + platform_set_drvdata(pdev, scm); ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr); if (ret < 0) return dev_err_probe(&pdev->dev, ret, diff --git a/drivers/firmware/qcom/qcom_scm.h b/drivers/firmware/qcom/qcom_scm.h index caab80a73e17..cf90a565fdfb 100644 --- a/drivers/firmware/qcom/qcom_scm.h +++ b/drivers/firmware/qcom/qcom_scm.h @@ -66,7 +66,7 @@ struct qcom_scm_res { u64 result[MAX_QCOM_SCM_RETS]; }; -int qcom_scm_wait_for_wq_completion(u32 wq_ctx); +int qcom_scm_wait_for_wq_completion(struct device *dev, u32 wq_ctx); int scm_get_wq_ctx(u32 *wq_ctx, u32 *flags, u32 *more_pending); #define SCM_SMC_FNID(s, c) ((((s) & 0xFF) << 8) | ((c) & 0xFF))