spi: bcm-qspi: Fix use-after-free on unbind
authorLukas Wunner <lukas@wunner.de>
Wed, 11 Nov 2020 19:07:40 +0000 (20:07 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 12 Nov 2020 15:05:37 +0000 (15:05 +0000)
bcm_qspi_remove() calls spi_unregister_master() even though
bcm_qspi_probe() calls devm_spi_register_master().  The spi_master is
therefore unregistered and freed twice on unbind.

Moreover, since commit 0392727c261b ("spi: bcm-qspi: Handle clock probe
deferral"), bcm_qspi_probe() leaks the spi_master allocation if the call
to devm_clk_get_optional() fails.

Fix by switching over to the new devm_spi_alloc_master() helper which
keeps the private data accessible until the driver has unbound and also
avoids the spi_master leak on probe.

While at it, fix an ordering issue in bcm_qspi_remove() wherein
spi_unregister_master() is called after uninitializing the hardware,
disabling the clock and freeing an IRQ data structure.  The correct
order is to call spi_unregister_master() *before* those teardown steps
because bus accesses may still be ongoing until that function returns.

Fixes: fa236a7ef240 ("spi: bcm-qspi: Add Broadcom MSPI driver")
Signed-off-by: Lukas Wunner <lukas@wunner.de>
Cc: <stable@vger.kernel.org> # v4.9+: 123456789abc: spi: Introduce device-managed SPI controller allocation
Cc: <stable@vger.kernel.org> # v4.9+
Cc: Kamal Dasu <kdasu.kdev@gmail.com>
Acked-by: Florian Fainelli <f.fainelli@gmail.com>
Tested-by: Florian Fainelli <f.fainelli@gmail.com>
Link: https://lore.kernel.org/r/5e31a9a59fd1c0d0b795b2fe219f25e5ee855f9d.1605121038.git.lukas@wunner.de
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-bcm-qspi.c

index 14c9d0133bce7db551fb2178cbb02e078c20b5eb..c028446c746045f0f830a291f9ddda7ce0bad460 100644 (file)
@@ -1327,7 +1327,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
 
        data = of_id->data;
 
-       master = spi_alloc_master(dev, sizeof(struct bcm_qspi));
+       master = devm_spi_alloc_master(dev, sizeof(struct bcm_qspi));
        if (!master) {
                dev_err(dev, "error allocating spi_master\n");
                return -ENOMEM;
@@ -1367,21 +1367,17 @@ int bcm_qspi_probe(struct platform_device *pdev,
 
        if (res) {
                qspi->base[MSPI]  = devm_ioremap_resource(dev, res);
-               if (IS_ERR(qspi->base[MSPI])) {
-                       ret = PTR_ERR(qspi->base[MSPI]);
-                       goto qspi_resource_err;
-               }
+               if (IS_ERR(qspi->base[MSPI]))
+                       return PTR_ERR(qspi->base[MSPI]);
        } else {
-               goto qspi_resource_err;
+               return 0;
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi");
        if (res) {
                qspi->base[BSPI]  = devm_ioremap_resource(dev, res);
-               if (IS_ERR(qspi->base[BSPI])) {
-                       ret = PTR_ERR(qspi->base[BSPI]);
-                       goto qspi_resource_err;
-               }
+               if (IS_ERR(qspi->base[BSPI]))
+                       return PTR_ERR(qspi->base[BSPI]);
                qspi->bspi_mode = true;
        } else {
                qspi->bspi_mode = false;
@@ -1392,18 +1388,14 @@ int bcm_qspi_probe(struct platform_device *pdev,
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs_reg");
        if (res) {
                qspi->base[CHIP_SELECT]  = devm_ioremap_resource(dev, res);
-               if (IS_ERR(qspi->base[CHIP_SELECT])) {
-                       ret = PTR_ERR(qspi->base[CHIP_SELECT]);
-                       goto qspi_resource_err;
-               }
+               if (IS_ERR(qspi->base[CHIP_SELECT]))
+                       return PTR_ERR(qspi->base[CHIP_SELECT]);
        }
 
        qspi->dev_ids = kcalloc(num_irqs, sizeof(struct bcm_qspi_dev_id),
                                GFP_KERNEL);
-       if (!qspi->dev_ids) {
-               ret = -ENOMEM;
-               goto qspi_resource_err;
-       }
+       if (!qspi->dev_ids)
+               return -ENOMEM;
 
        for (val = 0; val < num_irqs; val++) {
                irq = -1;
@@ -1484,7 +1476,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
        qspi->xfer_mode.addrlen = -1;
        qspi->xfer_mode.hp = -1;
 
-       ret = devm_spi_register_master(&pdev->dev, master);
+       ret = spi_register_master(master);
        if (ret < 0) {
                dev_err(dev, "can't register master\n");
                goto qspi_reg_err;
@@ -1497,8 +1489,6 @@ qspi_reg_err:
        clk_disable_unprepare(qspi->clk);
 qspi_probe_err:
        kfree(qspi->dev_ids);
-qspi_resource_err:
-       spi_master_put(master);
        return ret;
 }
 /* probe function to be called by SoC specific platform driver probe */
@@ -1508,10 +1498,10 @@ int bcm_qspi_remove(struct platform_device *pdev)
 {
        struct bcm_qspi *qspi = platform_get_drvdata(pdev);
 
+       spi_unregister_master(qspi->master);
        bcm_qspi_hw_uninit(qspi);
        clk_disable_unprepare(qspi->clk);
        kfree(qspi->dev_ids);
-       spi_unregister_master(qspi->master);
 
        return 0;
 }