spi: Fixes for v7.0

There are two core fixes here.  One is from Johan dealing with an issue
 introduced by a devm_ API usage update causing things to be freed
 earlier than they had earlier when we fail to register a device, another
 from Danilo avoids unlocked acccess to data by converting to use a
 driver core API.
 
 We also have a few relatively minor driver specific fixes.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmnG0MUACgkQJNaLcl1U
 h9BsrAf9H6Wj8EU/xkrHXl+trqY46+Fniloxp9Ar2aezYZXotnFL25JmAxyk5oYK
 HMHV677Xo+rcp9z77yr3vGyYFhiccioGm4t5kblIZxfwvxcuWj2r2d2KGF0jCBna
 qr3IfEowlgFW6BDfL72tZbuLrjkfmIAA2IMXOlZdHGz/QswNnOjPbbDRsyKn19xF
 a/CgVRyyAdAJxlv+p8/o7GbAMqJGStZ/w6Thk0GbLHASQE8Po1pTARGh7UbrJLVS
 hGEOM828PG8dctDbtE5Cg1mUm4rKIvtDlcZNLArSwPGfLcWei9AK++wfDck/ANho
 eRu8Zyr7yew00w+HLEssvyiSgS0HrA==
 =/ogD
 -----END PGP SIGNATURE-----

Merge tag 'spi-fix-v7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi fixes from Mark Brown:
 "There are two core fixes here. One is from Johan dealing with an issue
  introduced by a devm_ API usage update causing things to be freed
  earlier than they had earlier when we fail to register a device,
  another from Danilo avoids unlocked acccess to data by converting to
  use a driver core API.

  We also have a few relatively minor driver specific fixes"

* tag 'spi-fix-v7.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi:
  spi: spi-fsl-lpspi: fix teardown order issue (UAF)
  spi: fix use-after-free on managed registration failure
  spi: use generic driver_override infrastructure
  spi: meson-spicc: Fix double-put in remove path
  spi: sn-f-ospi: Use devm_mutex_init() to simplify code
  spi: sn-f-ospi: Fix resource leak in f_ospi_probe()
This commit is contained in:
Linus Torvalds 2026-03-27 16:38:55 -07:00
commit 335c9017e3
5 changed files with 31 additions and 53 deletions

View File

@ -1009,7 +1009,7 @@ static int fsl_lpspi_probe(struct platform_device *pdev)
enable_irq(irq);
}
ret = devm_spi_register_controller(&pdev->dev, controller);
ret = spi_register_controller(controller);
if (ret < 0) {
dev_err_probe(&pdev->dev, ret, "spi_register_controller error\n");
goto free_dma;
@ -1035,6 +1035,7 @@ static void fsl_lpspi_remove(struct platform_device *pdev)
struct fsl_lpspi_data *fsl_lpspi =
spi_controller_get_devdata(controller);
spi_unregister_controller(controller);
fsl_lpspi_dma_exit(controller);
pm_runtime_dont_use_autosuspend(fsl_lpspi->dev);

View File

@ -1101,8 +1101,6 @@ static void meson_spicc_remove(struct platform_device *pdev)
/* Disable SPI */
writel(0, spicc->base + SPICC_CONREG);
spi_controller_put(spicc->host);
}
static const struct meson_spicc_data meson_spicc_gx_data = {

View File

@ -612,7 +612,7 @@ static int f_ospi_probe(struct platform_device *pdev)
u32 num_cs = OSPI_NUM_CS;
int ret;
ctlr = spi_alloc_host(dev, sizeof(*ospi));
ctlr = devm_spi_alloc_host(dev, sizeof(*ospi));
if (!ctlr)
return -ENOMEM;
@ -635,43 +635,22 @@ static int f_ospi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ospi);
ospi->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ospi->base)) {
ret = PTR_ERR(ospi->base);
goto err_put_ctlr;
}
if (IS_ERR(ospi->base))
return PTR_ERR(ospi->base);
ospi->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(ospi->clk)) {
ret = PTR_ERR(ospi->clk);
goto err_put_ctlr;
}
if (IS_ERR(ospi->clk))
return PTR_ERR(ospi->clk);
mutex_init(&ospi->mlock);
ret = devm_mutex_init(dev, &ospi->mlock);
if (ret)
return ret;
ret = f_ospi_init(ospi);
if (ret)
goto err_destroy_mutex;
return ret;
ret = devm_spi_register_controller(dev, ctlr);
if (ret)
goto err_destroy_mutex;
return 0;
err_destroy_mutex:
mutex_destroy(&ospi->mlock);
err_put_ctlr:
spi_controller_put(ctlr);
return ret;
}
static void f_ospi_remove(struct platform_device *pdev)
{
struct f_ospi *ospi = platform_get_drvdata(pdev);
mutex_destroy(&ospi->mlock);
return devm_spi_register_controller(dev, ctlr);
}
static const struct of_device_id f_ospi_dt_ids[] = {
@ -686,7 +665,6 @@ static struct platform_driver f_ospi_driver = {
.of_match_table = f_ospi_dt_ids,
},
.probe = f_ospi_probe,
.remove = f_ospi_remove,
};
module_platform_driver(f_ospi_driver);

View File

@ -50,7 +50,6 @@ static void spidev_release(struct device *dev)
struct spi_device *spi = to_spi_device(dev);
spi_controller_put(spi->controller);
kfree(spi->driver_override);
free_percpu(spi->pcpu_statistics);
kfree(spi);
}
@ -73,10 +72,9 @@ static ssize_t driver_override_store(struct device *dev,
struct device_attribute *a,
const char *buf, size_t count)
{
struct spi_device *spi = to_spi_device(dev);
int ret;
ret = driver_set_override(dev, &spi->driver_override, buf, count);
ret = __device_set_driver_override(dev, buf, count);
if (ret)
return ret;
@ -86,13 +84,8 @@ static ssize_t driver_override_store(struct device *dev,
static ssize_t driver_override_show(struct device *dev,
struct device_attribute *a, char *buf)
{
const struct spi_device *spi = to_spi_device(dev);
ssize_t len;
device_lock(dev);
len = sysfs_emit(buf, "%s\n", spi->driver_override ? : "");
device_unlock(dev);
return len;
guard(spinlock)(&dev->driver_override.lock);
return sysfs_emit(buf, "%s\n", dev->driver_override.name ?: "");
}
static DEVICE_ATTR_RW(driver_override);
@ -376,10 +369,12 @@ static int spi_match_device(struct device *dev, const struct device_driver *drv)
{
const struct spi_device *spi = to_spi_device(dev);
const struct spi_driver *sdrv = to_spi_driver(drv);
int ret;
/* Check override first, and if set, only use the named driver */
if (spi->driver_override)
return strcmp(spi->driver_override, drv->name) == 0;
ret = device_match_driver_override(dev, drv);
if (ret >= 0)
return ret;
/* Attempt an OF style match */
if (of_driver_match_device(dev, drv))
@ -3539,8 +3534,19 @@ int devm_spi_register_controller(struct device *dev,
if (ret)
return ret;
return devm_add_action_or_reset(dev, devm_spi_unregister_controller, ctlr);
/*
* Prevent controller from being freed by spi_unregister_controller()
* if devm_add_action_or_reset() fails for a non-devres allocated
* controller.
*/
spi_controller_get(ctlr);
ret = devm_add_action_or_reset(dev, devm_spi_unregister_controller, ctlr);
if (ret == 0 || ctlr->devm_allocated)
spi_controller_put(ctlr);
return ret;
}
EXPORT_SYMBOL_GPL(devm_spi_register_controller);

View File

@ -159,10 +159,6 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
* @modalias: Name of the driver to use with this device, or an alias
* for that name. This appears in the sysfs "modalias" attribute
* for driver coldplugging, and in uevents used for hotplugging
* @driver_override: If the name of a driver is written to this attribute, then
* the device will bind to the named driver and only the named driver.
* Do not set directly, because core frees it; use driver_set_override() to
* set or clear it.
* @pcpu_statistics: statistics for the spi_device
* @word_delay: delay to be inserted between consecutive
* words of a transfer
@ -224,7 +220,6 @@ struct spi_device {
void *controller_state;
void *controller_data;
char modalias[SPI_NAME_SIZE];
const char *driver_override;
/* The statistics */
struct spi_statistics __percpu *pcpu_statistics;