OSDN Git Service

spi: stm32: Fix use-after-free on unbind
authorAlain Volmat <alain.volmat@foss.st.com>
Thu, 18 Mar 2021 07:24:50 +0000 (08:24 +0100)
committerMark Brown <broonie@kernel.org>
Fri, 19 Mar 2021 12:24:09 +0000 (12:24 +0000)
stm32_spi_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: 8d559a64f00b ("spi: stm32: drop devres version of spi_register_master")

Reported-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Alain Volmat <alain.volmat@foss.st.com>
Link: https://lore.kernel.org/r/1616052290-10887-1-git-send-email-alain.volmat@foss.st.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-stm32.c

index 3cc978e..aa2c501 100644 (file)
@@ -1803,7 +1803,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
        struct reset_control *rst;
        int ret;
 
-       master = spi_alloc_master(&pdev->dev, sizeof(struct stm32_spi));
+       master = devm_spi_alloc_master(&pdev->dev, sizeof(struct stm32_spi));
        if (!master) {
                dev_err(&pdev->dev, "spi master allocation failed\n");
                return -ENOMEM;
@@ -1821,18 +1821,16 @@ static int stm32_spi_probe(struct platform_device *pdev)
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        spi->base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(spi->base)) {
-               ret = PTR_ERR(spi->base);
-               goto err_master_put;
-       }
+       if (IS_ERR(spi->base))
+               return PTR_ERR(spi->base);
 
        spi->phys_addr = (dma_addr_t)res->start;
 
        spi->irq = platform_get_irq(pdev, 0);
-       if (spi->irq <= 0) {
-               ret = dev_err_probe(&pdev->dev, spi->irq, "failed to get irq\n");
-               goto err_master_put;
-       }
+       if (spi->irq <= 0)
+               return dev_err_probe(&pdev->dev, spi->irq,
+                                    "failed to get irq\n");
+
        ret = devm_request_threaded_irq(&pdev->dev, spi->irq,
                                        spi->cfg->irq_handler_event,
                                        spi->cfg->irq_handler_thread,
@@ -1840,20 +1838,20 @@ static int stm32_spi_probe(struct platform_device *pdev)
        if (ret) {
                dev_err(&pdev->dev, "irq%d request failed: %d\n", spi->irq,
                        ret);
-               goto err_master_put;
+               return ret;
        }
 
        spi->clk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(spi->clk)) {
                ret = PTR_ERR(spi->clk);
                dev_err(&pdev->dev, "clk get failed: %d\n", ret);
-               goto err_master_put;
+               return ret;
        }
 
        ret = clk_prepare_enable(spi->clk);
        if (ret) {
                dev_err(&pdev->dev, "clk enable failed: %d\n", ret);
-               goto err_master_put;
+               return ret;
        }
        spi->clk_rate = clk_get_rate(spi->clk);
        if (!spi->clk_rate) {
@@ -1949,8 +1947,6 @@ err_dma_release:
                dma_release_channel(spi->dma_rx);
 err_clk_disable:
        clk_disable_unprepare(spi->clk);
-err_master_put:
-       spi_master_put(master);
 
        return ret;
 }