spi: spi-qcom-qspi: Fix use-after-free on unbind
qcom_qspi_remove() accesses the driver's private data after calling
spi_unregister_master() even though that function releases the last
reference on the spi_master and thereby frees the private data.
Fix by switching over to the new devm_spi_alloc_master() helper which
keeps the private data accessible until the driver has unbound.
Fixes: f79a158d37
("spi: spi-qcom-qspi: Use OPP API to set clk/perf state")
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Cc: <stable@vger.kernel.org> # v5.9+: 5e844cc37a5c: spi: Introduce device-managed SPI controller allocation
Cc: <stable@vger.kernel.org> # v5.9+
Cc: Rajendra Nayak <rnayak@codeaurora.org>
Link: https://lore.kernel.org/r/b6d3c4dce571d78a532fd74f27def0d5dc8d8a24.1607286887.git.lukas@wunner.de
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Родитель
8f96c434df
Коммит
6cfd39e212
|
@ -462,7 +462,7 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
|||
|
||||
dev = &pdev->dev;
|
||||
|
||||
master = spi_alloc_master(dev, sizeof(*ctrl));
|
||||
master = devm_spi_alloc_master(dev, sizeof(*ctrl));
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -473,54 +473,49 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
|||
spin_lock_init(&ctrl->lock);
|
||||
ctrl->dev = dev;
|
||||
ctrl->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctrl->base)) {
|
||||
ret = PTR_ERR(ctrl->base);
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (IS_ERR(ctrl->base))
|
||||
return PTR_ERR(ctrl->base);
|
||||
|
||||
ctrl->clks = devm_kcalloc(dev, QSPI_NUM_CLKS,
|
||||
sizeof(*ctrl->clks), GFP_KERNEL);
|
||||
if (!ctrl->clks) {
|
||||
ret = -ENOMEM;
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (!ctrl->clks)
|
||||
return -ENOMEM;
|
||||
|
||||
ctrl->clks[QSPI_CLK_CORE].id = "core";
|
||||
ctrl->clks[QSPI_CLK_IFACE].id = "iface";
|
||||
ret = devm_clk_bulk_get(dev, QSPI_NUM_CLKS, ctrl->clks);
|
||||
if (ret)
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
|
||||
ctrl->icc_path_cpu_to_qspi = devm_of_icc_get(dev, "qspi-config");
|
||||
if (IS_ERR(ctrl->icc_path_cpu_to_qspi)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi),
|
||||
"Failed to get cpu path\n");
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (IS_ERR(ctrl->icc_path_cpu_to_qspi))
|
||||
return dev_err_probe(dev, PTR_ERR(ctrl->icc_path_cpu_to_qspi),
|
||||
"Failed to get cpu path\n");
|
||||
|
||||
/* Set BW vote for register access */
|
||||
ret = icc_set_bw(ctrl->icc_path_cpu_to_qspi, Bps_to_icc(1000),
|
||||
Bps_to_icc(1000));
|
||||
if (ret) {
|
||||
dev_err(ctrl->dev, "%s: ICC BW voting failed for cpu: %d\n",
|
||||
__func__, ret);
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = icc_disable(ctrl->icc_path_cpu_to_qspi);
|
||||
if (ret) {
|
||||
dev_err(ctrl->dev, "%s: ICC disable failed for cpu: %d\n",
|
||||
__func__, ret);
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
ret = devm_request_irq(dev, ret, qcom_qspi_irq,
|
||||
IRQF_TRIGGER_HIGH, dev_name(dev), ctrl);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request irq %d\n", ret);
|
||||
goto exit_probe_master_put;
|
||||
return ret;
|
||||
}
|
||||
|
||||
master->max_speed_hz = 300000000;
|
||||
|
@ -537,10 +532,8 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
|||
master->auto_runtime_pm = true;
|
||||
|
||||
ctrl->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "core");
|
||||
if (IS_ERR(ctrl->opp_table)) {
|
||||
ret = PTR_ERR(ctrl->opp_table);
|
||||
goto exit_probe_master_put;
|
||||
}
|
||||
if (IS_ERR(ctrl->opp_table))
|
||||
return PTR_ERR(ctrl->opp_table);
|
||||
/* OPP table is optional */
|
||||
ret = dev_pm_opp_of_add_table(&pdev->dev);
|
||||
if (ret && ret != -ENODEV) {
|
||||
|
@ -562,9 +555,6 @@ static int qcom_qspi_probe(struct platform_device *pdev)
|
|||
exit_probe_put_clkname:
|
||||
dev_pm_opp_put_clkname(ctrl->opp_table);
|
||||
|
||||
exit_probe_master_put:
|
||||
spi_master_put(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче