]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
spi: fix use-after-free on managed registration failure
authorJohan Hovold <johan@kernel.org>
Wed, 25 Mar 2026 14:53:19 +0000 (15:53 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 26 Mar 2026 10:45:28 +0000 (10:45 +0000)
The SPI API is asymmetric and the controller is freed as part of
deregistration (unless it has been allocated using
devm_spi_alloc_host/target()).

A recent change converting the managed registration function to use
devm_add_action_or_reset() inadvertently introduced a (mostly
theoretical) regression where a non-devres managed controller could be
freed as part of failed registration. This in turn would lead to
use-after-free in controller driver error paths.

Fix this by taking another reference before calling
devm_add_action_or_reset() and not releasing it on errors for
non-devres allocated controllers.

An alternative would be a partial revert of the offending commit, but
it is better to handle this explicitly until the API has been fixed
(e.g. see 5e844cc37a5c ("spi: Introduce device-managed SPI controller
allocation")).

Fixes: b6376dbed8e1 ("spi: Simplify devm_spi_*_controller()")
Reported-by: Felix Gu <ustc.gu@gmail.com>
Link: https://lore.kernel.org/all/20260324145548.139952-1-ustc.gu@gmail.com/
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Johan Hovold <johan@kernel.org>
Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://patch.msgid.link/20260325145319.1132072-1-johan@kernel.org
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi.c

index 4101c2803eb31e67fc5c28a51043ac2bf7b751ba..9b1125556d29572521c9a8f7e6656bc454fc8a59 100644 (file)
@@ -3534,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);