Commit c371dcf5 authored by Mark Brown's avatar Mark Brown

Merge series "Use-after-free be gone" from Lukas Wunner <lukas@wunner.de>:

Here's my proposal to fix the use-after-free bugs reported by
Sascha Hauer and Florian Fainelli:

I scrutinized all SPI drivers in the v5.10 tree:

* There are 9 drivers with a use-after-free in the ->remove() hook
  caused by accessing driver private data after spi_unregister_controller().

* There are 8 drivers which leak the spi_controller in the ->probe()
  error path because of a missing spi_controller_put().

I'm introducing devm_spi_alloc_master/slave() which automatically
calls spi_controller_put() on ->remove().  This fixes both classes
of bugs while at the same time reducing code amount and complexity
in the ->probe() hook.

I propose that spi_controller_unregister() should no longer release
a reference on the spi_controller.  Instead, drivers need to either
do it themselves or use one of the devm functions introduced herein.
The vast majority of drivers can be converted to the devm functions.
See the commit message of patch [1/4] for the rationale and details.

Enclosed are patches for 3 Broadcom drivers.
Patches for the other drivers are on this branch:
https://github.com/l1k/linux/commits/spi_fixes

@Florian Fainelli:  Could you verify that there are no KASAN splats or
leaks with these patches?  Unfortunately I do not have any SPI-capable
hardware at my disposal right now, so can only compile-test.  You may
want to augment spi_controller_release() with a printk() to log when
the spi_controller is freed.

@Mark Brown:  Patches [2/4] to [4/4] reference the SHA-1 of patch [1/4]
in their stable tags.  Because the hash is unknown to me until you apply
the patch, I've used "123456789abc" as a placeholder.  You'll have to
replace the hash if/when applying.  Alternatively, only apply patch [1/4]
and I'll repost the other patches with the hash fixed up.

Thanks!

Lukas Wunner (4):
  spi: Introduce device-managed SPI controller allocation
  spi: bcm2835: Fix use-after-free on unbind
  spi: bcm2835aux: Fix use-after-free on unbind
  spi: bcm-qspi: Fix use-after-free on unbind

 drivers/spi/spi-bcm-qspi.c   | 34 ++++++++-------------
 drivers/spi/spi-bcm2835.c    | 24 +++++----------
 drivers/spi/spi-bcm2835aux.c | 21 +++++--------
 drivers/spi/spi.c            | 58 +++++++++++++++++++++++++++++++++++-
 include/linux/spi/spi.h      | 19 ++++++++++++
 5 files changed, 103 insertions(+), 53 deletions(-)

--
2.28.0
parents 4def49da 63c5395b
...@@ -1327,7 +1327,7 @@ int bcm_qspi_probe(struct platform_device *pdev, ...@@ -1327,7 +1327,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
data = of_id->data; 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) { if (!master) {
dev_err(dev, "error allocating spi_master\n"); dev_err(dev, "error allocating spi_master\n");
return -ENOMEM; return -ENOMEM;
...@@ -1367,21 +1367,17 @@ int bcm_qspi_probe(struct platform_device *pdev, ...@@ -1367,21 +1367,17 @@ int bcm_qspi_probe(struct platform_device *pdev,
if (res) { if (res) {
qspi->base[MSPI] = devm_ioremap_resource(dev, res); qspi->base[MSPI] = devm_ioremap_resource(dev, res);
if (IS_ERR(qspi->base[MSPI])) { if (IS_ERR(qspi->base[MSPI]))
ret = PTR_ERR(qspi->base[MSPI]); return PTR_ERR(qspi->base[MSPI]);
goto qspi_resource_err;
}
} else { } else {
goto qspi_resource_err; return 0;
} }
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi");
if (res) { if (res) {
qspi->base[BSPI] = devm_ioremap_resource(dev, res); qspi->base[BSPI] = devm_ioremap_resource(dev, res);
if (IS_ERR(qspi->base[BSPI])) { if (IS_ERR(qspi->base[BSPI]))
ret = PTR_ERR(qspi->base[BSPI]); return PTR_ERR(qspi->base[BSPI]);
goto qspi_resource_err;
}
qspi->bspi_mode = true; qspi->bspi_mode = true;
} else { } else {
qspi->bspi_mode = false; qspi->bspi_mode = false;
...@@ -1392,18 +1388,14 @@ int bcm_qspi_probe(struct platform_device *pdev, ...@@ -1392,18 +1388,14 @@ int bcm_qspi_probe(struct platform_device *pdev,
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs_reg"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cs_reg");
if (res) { if (res) {
qspi->base[CHIP_SELECT] = devm_ioremap_resource(dev, res); qspi->base[CHIP_SELECT] = devm_ioremap_resource(dev, res);
if (IS_ERR(qspi->base[CHIP_SELECT])) { if (IS_ERR(qspi->base[CHIP_SELECT]))
ret = PTR_ERR(qspi->base[CHIP_SELECT]); return PTR_ERR(qspi->base[CHIP_SELECT]);
goto qspi_resource_err;
}
} }
qspi->dev_ids = kcalloc(num_irqs, sizeof(struct bcm_qspi_dev_id), qspi->dev_ids = kcalloc(num_irqs, sizeof(struct bcm_qspi_dev_id),
GFP_KERNEL); GFP_KERNEL);
if (!qspi->dev_ids) { if (!qspi->dev_ids)
ret = -ENOMEM; return -ENOMEM;
goto qspi_resource_err;
}
for (val = 0; val < num_irqs; val++) { for (val = 0; val < num_irqs; val++) {
irq = -1; irq = -1;
...@@ -1484,7 +1476,7 @@ int bcm_qspi_probe(struct platform_device *pdev, ...@@ -1484,7 +1476,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
qspi->xfer_mode.addrlen = -1; qspi->xfer_mode.addrlen = -1;
qspi->xfer_mode.hp = -1; qspi->xfer_mode.hp = -1;
ret = devm_spi_register_master(&pdev->dev, master); ret = spi_register_master(master);
if (ret < 0) { if (ret < 0) {
dev_err(dev, "can't register master\n"); dev_err(dev, "can't register master\n");
goto qspi_reg_err; goto qspi_reg_err;
...@@ -1497,8 +1489,6 @@ int bcm_qspi_probe(struct platform_device *pdev, ...@@ -1497,8 +1489,6 @@ int bcm_qspi_probe(struct platform_device *pdev,
clk_disable_unprepare(qspi->clk); clk_disable_unprepare(qspi->clk);
qspi_probe_err: qspi_probe_err:
kfree(qspi->dev_ids); kfree(qspi->dev_ids);
qspi_resource_err:
spi_master_put(master);
return ret; return ret;
} }
/* probe function to be called by SoC specific platform driver probe */ /* probe function to be called by SoC specific platform driver probe */
...@@ -1508,10 +1498,10 @@ int bcm_qspi_remove(struct platform_device *pdev) ...@@ -1508,10 +1498,10 @@ int bcm_qspi_remove(struct platform_device *pdev)
{ {
struct bcm_qspi *qspi = platform_get_drvdata(pdev); struct bcm_qspi *qspi = platform_get_drvdata(pdev);
spi_unregister_master(qspi->master);
bcm_qspi_hw_uninit(qspi); bcm_qspi_hw_uninit(qspi);
clk_disable_unprepare(qspi->clk); clk_disable_unprepare(qspi->clk);
kfree(qspi->dev_ids); kfree(qspi->dev_ids);
spi_unregister_master(qspi->master);
return 0; return 0;
} }
......
...@@ -1278,7 +1278,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev) ...@@ -1278,7 +1278,7 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
struct bcm2835_spi *bs; struct bcm2835_spi *bs;
int err; int err;
ctlr = spi_alloc_master(&pdev->dev, ALIGN(sizeof(*bs), ctlr = devm_spi_alloc_master(&pdev->dev, ALIGN(sizeof(*bs),
dma_get_cache_alignment())); dma_get_cache_alignment()));
if (!ctlr) if (!ctlr)
return -ENOMEM; return -ENOMEM;
...@@ -1299,23 +1299,17 @@ static int bcm2835_spi_probe(struct platform_device *pdev) ...@@ -1299,23 +1299,17 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
bs->ctlr = ctlr; bs->ctlr = ctlr;
bs->regs = devm_platform_ioremap_resource(pdev, 0); bs->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bs->regs)) { if (IS_ERR(bs->regs))
err = PTR_ERR(bs->regs); return PTR_ERR(bs->regs);
goto out_controller_put;
}
bs->clk = devm_clk_get(&pdev->dev, NULL); bs->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(bs->clk)) { if (IS_ERR(bs->clk))
err = dev_err_probe(&pdev->dev, PTR_ERR(bs->clk), return dev_err_probe(&pdev->dev, PTR_ERR(bs->clk),
"could not get clk\n"); "could not get clk\n");
goto out_controller_put;
}
bs->irq = platform_get_irq(pdev, 0); bs->irq = platform_get_irq(pdev, 0);
if (bs->irq <= 0) { if (bs->irq <= 0)
err = bs->irq ? bs->irq : -ENODEV; return bs->irq ? bs->irq : -ENODEV;
goto out_controller_put;
}
clk_prepare_enable(bs->clk); clk_prepare_enable(bs->clk);
...@@ -1349,8 +1343,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev) ...@@ -1349,8 +1343,6 @@ static int bcm2835_spi_probe(struct platform_device *pdev)
bcm2835_dma_release(ctlr, bs); bcm2835_dma_release(ctlr, bs);
out_clk_disable: out_clk_disable:
clk_disable_unprepare(bs->clk); clk_disable_unprepare(bs->clk);
out_controller_put:
spi_controller_put(ctlr);
return err; return err;
} }
......
...@@ -494,7 +494,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) ...@@ -494,7 +494,7 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)
unsigned long clk_hz; unsigned long clk_hz;
int err; int err;
master = spi_alloc_master(&pdev->dev, sizeof(*bs)); master = devm_spi_alloc_master(&pdev->dev, sizeof(*bs));
if (!master) if (!master)
return -ENOMEM; return -ENOMEM;
...@@ -524,29 +524,24 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) ...@@ -524,29 +524,24 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)
/* the main area */ /* the main area */
bs->regs = devm_platform_ioremap_resource(pdev, 0); bs->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(bs->regs)) { if (IS_ERR(bs->regs))
err = PTR_ERR(bs->regs); return PTR_ERR(bs->regs);
goto out_master_put;
}
bs->clk = devm_clk_get(&pdev->dev, NULL); bs->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(bs->clk)) { if (IS_ERR(bs->clk)) {
err = PTR_ERR(bs->clk);
dev_err(&pdev->dev, "could not get clk: %d\n", err); dev_err(&pdev->dev, "could not get clk: %d\n", err);
goto out_master_put; return PTR_ERR(bs->clk);
} }
bs->irq = platform_get_irq(pdev, 0); bs->irq = platform_get_irq(pdev, 0);
if (bs->irq <= 0) { if (bs->irq <= 0)
err = bs->irq ? bs->irq : -ENODEV; return bs->irq ? bs->irq : -ENODEV;
goto out_master_put;
}
/* this also enables the HW block */ /* this also enables the HW block */
err = clk_prepare_enable(bs->clk); err = clk_prepare_enable(bs->clk);
if (err) { if (err) {
dev_err(&pdev->dev, "could not prepare clock: %d\n", err); dev_err(&pdev->dev, "could not prepare clock: %d\n", err);
goto out_master_put; return err;
} }
/* just checking if the clock returns a sane value */ /* just checking if the clock returns a sane value */
...@@ -581,8 +576,6 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev) ...@@ -581,8 +576,6 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)
out_clk_disable: out_clk_disable:
clk_disable_unprepare(bs->clk); clk_disable_unprepare(bs->clk);
out_master_put:
spi_master_put(master);
return err; return err;
} }
......
...@@ -2442,6 +2442,49 @@ struct spi_controller *__spi_alloc_controller(struct device *dev, ...@@ -2442,6 +2442,49 @@ struct spi_controller *__spi_alloc_controller(struct device *dev,
} }
EXPORT_SYMBOL_GPL(__spi_alloc_controller); EXPORT_SYMBOL_GPL(__spi_alloc_controller);
static void devm_spi_release_controller(struct device *dev, void *ctlr)
{
spi_controller_put(*(struct spi_controller **)ctlr);
}
/**
* __devm_spi_alloc_controller - resource-managed __spi_alloc_controller()
* @dev: physical device of SPI controller
* @size: how much zeroed driver-private data to allocate
* @slave: whether to allocate an SPI master (false) or SPI slave (true)
* Context: can sleep
*
* Allocate an SPI controller and automatically release a reference on it
* when @dev is unbound from its driver. Drivers are thus relieved from
* having to call spi_controller_put().
*
* The arguments to this function are identical to __spi_alloc_controller().
*
* Return: the SPI controller structure on success, else NULL.
*/
struct spi_controller *__devm_spi_alloc_controller(struct device *dev,
unsigned int size,
bool slave)
{
struct spi_controller **ptr, *ctlr;
ptr = devres_alloc(devm_spi_release_controller, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return NULL;
ctlr = __spi_alloc_controller(dev, size, slave);
if (ctlr) {
*ptr = ctlr;
devres_add(dev, ptr);
} else {
devres_free(ptr);
}
return ctlr;
}
EXPORT_SYMBOL_GPL(__devm_spi_alloc_controller);
#ifdef CONFIG_OF #ifdef CONFIG_OF
static int of_spi_get_gpio_numbers(struct spi_controller *ctlr) static int of_spi_get_gpio_numbers(struct spi_controller *ctlr)
{ {
...@@ -2778,6 +2821,11 @@ int devm_spi_register_controller(struct device *dev, ...@@ -2778,6 +2821,11 @@ int devm_spi_register_controller(struct device *dev,
} }
EXPORT_SYMBOL_GPL(devm_spi_register_controller); EXPORT_SYMBOL_GPL(devm_spi_register_controller);
static int devm_spi_match_controller(struct device *dev, void *res, void *ctlr)
{
return *(struct spi_controller **)res == ctlr;
}
static int __unregister(struct device *dev, void *null) static int __unregister(struct device *dev, void *null)
{ {
spi_unregister_device(to_spi_device(dev)); spi_unregister_device(to_spi_device(dev));
...@@ -2819,7 +2867,15 @@ void spi_unregister_controller(struct spi_controller *ctlr) ...@@ -2819,7 +2867,15 @@ void spi_unregister_controller(struct spi_controller *ctlr)
list_del(&ctlr->list); list_del(&ctlr->list);
mutex_unlock(&board_lock); mutex_unlock(&board_lock);
device_unregister(&ctlr->dev); device_del(&ctlr->dev);
/* Release the last reference on the controller if its driver
* has not yet been converted to devm_spi_alloc_master/slave().
*/
if (!devres_find(ctlr->dev.parent, devm_spi_release_controller,
devm_spi_match_controller, ctlr))
put_device(&ctlr->dev);
/* free bus id */ /* free bus id */
mutex_lock(&board_lock); mutex_lock(&board_lock);
if (found == ctlr) if (found == ctlr)
......
...@@ -734,6 +734,25 @@ static inline struct spi_controller *spi_alloc_slave(struct device *host, ...@@ -734,6 +734,25 @@ static inline struct spi_controller *spi_alloc_slave(struct device *host,
return __spi_alloc_controller(host, size, true); return __spi_alloc_controller(host, size, true);
} }
struct spi_controller *__devm_spi_alloc_controller(struct device *dev,
unsigned int size,
bool slave);
static inline struct spi_controller *devm_spi_alloc_master(struct device *dev,
unsigned int size)
{
return __devm_spi_alloc_controller(dev, size, false);
}
static inline struct spi_controller *devm_spi_alloc_slave(struct device *dev,
unsigned int size)
{
if (!IS_ENABLED(CONFIG_SPI_SLAVE))
return NULL;
return __devm_spi_alloc_controller(dev, size, true);
}
extern int spi_register_controller(struct spi_controller *ctlr); extern int spi_register_controller(struct spi_controller *ctlr);
extern int devm_spi_register_controller(struct device *dev, extern int devm_spi_register_controller(struct device *dev,
struct spi_controller *ctlr); struct spi_controller *ctlr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment