mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 05:04:02 +02:00
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: 6bf3259922 ("firmware: qcom: scm: Add wait-queue handling logic")
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20260724094939.613844-2-mukesh.ojha@oss.qualcomm.com
Signed-off-by: Bjorn Andersson <andersson@kernel.org>
This commit is contained in:
parent
ca8c96d318
commit
966d23c7e6
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user