summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-qcom-qspi.c
diff options
context:
space:
mode:
authorLukas Wunner <lukas@wunner.de>2020-12-07 09:17:03 +0100
committerMark Brown <broonie@kernel.org>2020-12-07 14:18:49 +0000
commit6cfd39e212dee2e77a0227ce4e0f55fa06d79f46 (patch)
tree616ba286d0e51b9192d07a91ffeb5fb6ebe0024c /drivers/spi/spi-qcom-qspi.c
parent8f96c434dfbc85ffa755d6634c8c1cb2233fcf24 (diff)
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: f79a158d37c2 ("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>
Diffstat (limited to 'drivers/spi/spi-qcom-qspi.c')
-rw-r--r--drivers/spi/spi-qcom-qspi.c42
1 files changed, 16 insertions, 26 deletions
diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c
index 5eed88af6899..8863be370884 100644
--- a/drivers/spi/spi-qcom-qspi.c
+++ b/drivers/spi/spi-qcom-qspi.c
@@ -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;
}