diff mbox series

spi: Fix use-after-free with devm_spi_alloc_*

Message ID 20210407095527.2771582-1-wak@google.com
State Accepted
Commit 794aaf01444d4e765e2b067cba01cc69c1c68ed9
Headers show
Series spi: Fix use-after-free with devm_spi_alloc_* | expand

Commit Message

William Kennington April 7, 2021, 9:55 a.m. UTC
We can't rely on the contents of the devres list during
spi_unregister_controller(), as the list is already torn down at the
time we perform devres_find() for devm_spi_release_controller. This
causes devices registered with devm_spi_alloc_{master,slave}() to be
mistakenly identified as legacy, non-devm managed devices and have their
reference counters decremented below 0.

------------[ cut here ]------------
WARNING: CPU: 1 PID: 660 at lib/refcount.c:28 refcount_warn_saturate+0x108/0x174
[<b0396f04>] (refcount_warn_saturate) from [<b03c56a4>] (kobject_put+0x90/0x98)
[<b03c5614>] (kobject_put) from [<b0447b4c>] (put_device+0x20/0x24)
 r4:b6700140
[<b0447b2c>] (put_device) from [<b07515e8>] (devm_spi_release_controller+0x3c/0x40)
[<b07515ac>] (devm_spi_release_controller) from [<b045343c>] (release_nodes+0x84/0xc4)
 r5:b6700180 r4:b6700100
[<b04533b8>] (release_nodes) from [<b0454160>] (devres_release_all+0x5c/0x60)
 r8:b1638c54 r7:b117ad94 r6:b1638c10 r5:b117ad94 r4:b163dc10
[<b0454104>] (devres_release_all) from [<b044e41c>] (__device_release_driver+0x144/0x1ec)
 r5:b117ad94 r4:b163dc10
[<b044e2d8>] (__device_release_driver) from [<b044f70c>] (device_driver_detach+0x84/0xa0)
 r9:00000000 r8:00000000 r7:b117ad94 r6:b163dc54 r5:b1638c10 r4:b163dc10
[<b044f688>] (device_driver_detach) from [<b044d274>] (unbind_store+0xe4/0xf8)

Instead, determine the devm allocation state as a flag on the
controller which is guaranteed to be stable during cleanup.

Fixes: 5e844cc37a5c ("spi: Introduce device-managed SPI controller allocation")
Signed-off-by: William A. Kennington III <wak@google.com>
---
 drivers/spi/spi.c       | 9 ++-------
 include/linux/spi/spi.h | 3 +++
 2 files changed, 5 insertions(+), 7 deletions(-)

Comments

Mark Brown April 8, 2021, 4:54 p.m. UTC | #1
On Wed, 7 Apr 2021 02:55:27 -0700, William A. Kennington III wrote:
> We can't rely on the contents of the devres list during
> spi_unregister_controller(), as the list is already torn down at the
> time we perform devres_find() for devm_spi_release_controller. This
> causes devices registered with devm_spi_alloc_{master,slave}() to be
> mistakenly identified as legacy, non-devm managed devices and have their
> reference counters decremented below 0.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next

Thanks!

[1/1] spi: Fix use-after-free with devm_spi_alloc_*
      commit: 794aaf01444d4e765e2b067cba01cc69c1c68ed9

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark
Joel Stanley April 9, 2021, 6:59 a.m. UTC | #2
On Wed, 7 Apr 2021 at 09:55, William A. Kennington III <wak@google.com> wrote:
>

> We can't rely on the contents of the devres list during

> spi_unregister_controller(), as the list is already torn down at the

> time we perform devres_find() for devm_spi_release_controller. This

> causes devices registered with devm_spi_alloc_{master,slave}() to be

> mistakenly identified as legacy, non-devm managed devices and have their

> reference counters decremented below 0.


Thanks for spending the time to track down the bug and sending a fix
for it. I appreciate it!

Reviewed-by: Joel Stnaley <joel@jms.id.au>


Cheers,

Joel

>

> ------------[ cut here ]------------

> WARNING: CPU: 1 PID: 660 at lib/refcount.c:28 refcount_warn_saturate+0x108/0x174

> [<b0396f04>] (refcount_warn_saturate) from [<b03c56a4>] (kobject_put+0x90/0x98)

> [<b03c5614>] (kobject_put) from [<b0447b4c>] (put_device+0x20/0x24)

>  r4:b6700140

> [<b0447b2c>] (put_device) from [<b07515e8>] (devm_spi_release_controller+0x3c/0x40)

> [<b07515ac>] (devm_spi_release_controller) from [<b045343c>] (release_nodes+0x84/0xc4)

>  r5:b6700180 r4:b6700100

> [<b04533b8>] (release_nodes) from [<b0454160>] (devres_release_all+0x5c/0x60)

>  r8:b1638c54 r7:b117ad94 r6:b1638c10 r5:b117ad94 r4:b163dc10

> [<b0454104>] (devres_release_all) from [<b044e41c>] (__device_release_driver+0x144/0x1ec)

>  r5:b117ad94 r4:b163dc10

> [<b044e2d8>] (__device_release_driver) from [<b044f70c>] (device_driver_detach+0x84/0xa0)

>  r9:00000000 r8:00000000 r7:b117ad94 r6:b163dc54 r5:b1638c10 r4:b163dc10

> [<b044f688>] (device_driver_detach) from [<b044d274>] (unbind_store+0xe4/0xf8)

>

> Instead, determine the devm allocation state as a flag on the

> controller which is guaranteed to be stable during cleanup.

>

> Fixes: 5e844cc37a5c ("spi: Introduce device-managed SPI controller allocation")

> Signed-off-by: William A. Kennington III <wak@google.com>

> ---

>  drivers/spi/spi.c       | 9 ++-------

>  include/linux/spi/spi.h | 3 +++

>  2 files changed, 5 insertions(+), 7 deletions(-)

>

> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c

> index b08efe88ccd6..904a353798b6 100644

> --- a/drivers/spi/spi.c

> +++ b/drivers/spi/spi.c

> @@ -2496,6 +2496,7 @@ struct spi_controller *__devm_spi_alloc_controller(struct device *dev,

>

>         ctlr = __spi_alloc_controller(dev, size, slave);

>         if (ctlr) {

> +               ctlr->devm_allocated = true;

>                 *ptr = ctlr;

>                 devres_add(dev, ptr);

>         } else {

> @@ -2842,11 +2843,6 @@ int devm_spi_register_controller(struct device *dev,

>  }

>  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)

>  {

>         spi_unregister_device(to_spi_device(dev));

> @@ -2893,8 +2889,7 @@ void spi_unregister_controller(struct spi_controller *ctlr)

>         /* 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))

> +       if (!ctlr->devm_allocated)

>                 put_device(&ctlr->dev);

>

>         /* free bus id */

> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h

> index 592897fa4f03..643139b1eafe 100644

> --- a/include/linux/spi/spi.h

> +++ b/include/linux/spi/spi.h

> @@ -510,6 +510,9 @@ struct spi_controller {

>

>  #define SPI_MASTER_GPIO_SS             BIT(5)  /* GPIO CS must select slave */

>

> +       /* flag indicating this is a non-devres managed controller */

> +       bool                    devm_allocated;

> +

>         /* flag indicating this is an SPI slave controller */

>         bool                    slave;

>

> --

> 2.31.0.208.g409f899ff0-goog

>
William Kennington April 9, 2021, 9:05 a.m. UTC | #3
For the header file comment? I think it's actually backwards since
`devm_allocated = true` would indicate the device is managed with
devres. Should I send a followup change or v2?


On Fri, Apr 9, 2021 at 12:20 AM Andy Shevchenko
<andy.shevchenko@gmail.com> wrote:
>

>

>

> On Wednesday, April 7, 2021, William A. Kennington III <wak@google.com> wrote:

>>

>> We can't rely on the contents of the devres list during

>> spi_unregister_controller(), as the list is already torn down at the

>> time we perform devres_find() for devm_spi_release_controller. This

>> causes devices registered with devm_spi_alloc_{master,slave}() to be

>> mistakenly identified as legacy, non-devm managed devices and have their

>> reference counters decremented below 0.

>>

>> ------------[ cut here ]------------

>> WARNING: CPU: 1 PID: 660 at lib/refcount.c:28 refcount_warn_saturate+0x108/0x174

>> [<b0396f04>] (refcount_warn_saturate) from [<b03c56a4>] (kobject_put+0x90/0x98)

>> [<b03c5614>] (kobject_put) from [<b0447b4c>] (put_device+0x20/0x24)

>>  r4:b6700140

>> [<b0447b2c>] (put_device) from [<b07515e8>] (devm_spi_release_controller+0x3c/0x40)

>> [<b07515ac>] (devm_spi_release_controller) from [<b045343c>] (release_nodes+0x84/0xc4)

>>  r5:b6700180 r4:b6700100

>> [<b04533b8>] (release_nodes) from [<b0454160>] (devres_release_all+0x5c/0x60)

>>  r8:b1638c54 r7:b117ad94 r6:b1638c10 r5:b117ad94 r4:b163dc10

>> [<b0454104>] (devres_release_all) from [<b044e41c>] (__device_release_driver+0x144/0x1ec)

>>  r5:b117ad94 r4:b163dc10

>> [<b044e2d8>] (__device_release_driver) from [<b044f70c>] (device_driver_detach+0x84/0xa0)

>>  r9:00000000 r8:00000000 r7:b117ad94 r6:b163dc54 r5:b1638c10 r4:b163dc10

>> [<b044f688>] (device_driver_detach) from [<b044d274>] (unbind_store+0xe4/0xf8)

>>

>> Instead, determine the devm allocation state as a flag on the

>> controller which is guaranteed to be stable during cleanup.

>>

>> Fixes: 5e844cc37a5c ("spi: Introduce device-managed SPI controller allocation")

>> Signed-off-by: William A. Kennington III <wak@google.com>

>> ---

>>  drivers/spi/spi.c       | 9 ++-------

>>  include/linux/spi/spi.h | 3 +++

>>  2 files changed, 5 insertions(+), 7 deletions(-)

>>

>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c

>> index b08efe88ccd6..904a353798b6 100644

>> --- a/drivers/spi/spi.c

>> +++ b/drivers/spi/spi.c

>> @@ -2496,6 +2496,7 @@ struct spi_controller *__devm_spi_alloc_controller(struct device *dev,

>>

>>         ctlr = __spi_alloc_controller(dev, size, slave);

>>         if (ctlr) {

>> +               ctlr->devm_allocated = true;

>>                 *ptr = ctlr;

>>                 devres_add(dev, ptr);

>>         } else {

>> @@ -2842,11 +2843,6 @@ int devm_spi_register_controller(struct device *dev,

>>  }

>>  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)

>>  {

>>         spi_unregister_device(to_spi_device(dev));

>> @@ -2893,8 +2889,7 @@ void spi_unregister_controller(struct spi_controller *ctlr)

>>         /* 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))

>> +       if (!ctlr->devm_allocated)

>>                 put_device(&ctlr->dev);

>>

>>         /* free bus id */

>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h

>> index 592897fa4f03..643139b1eafe 100644

>> --- a/include/linux/spi/spi.h

>> +++ b/include/linux/spi/spi.h

>> @@ -510,6 +510,9 @@ struct spi_controller {

>>

>>  #define SPI_MASTER_GPIO_SS             BIT(5)  /* GPIO CS must select slave */

>>

>> +       /* flag indicating this is a non-devres managed controller */

>

>

>

> Isn’t “non-“ part confusing a lot?

>

>>

>> +       bool                    devm_allocated;

>> +

>>         /* flag indicating this is an SPI slave controller */

>>         bool                    slave;

>>

>> --

>> 2.31.0.208.g409f899ff0-goog

>>

>

>

> --

> With Best Regards,

> Andy Shevchenko

>

>
diff mbox series

Patch

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index b08efe88ccd6..904a353798b6 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2496,6 +2496,7 @@  struct spi_controller *__devm_spi_alloc_controller(struct device *dev,
 
 	ctlr = __spi_alloc_controller(dev, size, slave);
 	if (ctlr) {
+		ctlr->devm_allocated = true;
 		*ptr = ctlr;
 		devres_add(dev, ptr);
 	} else {
@@ -2842,11 +2843,6 @@  int devm_spi_register_controller(struct device *dev,
 }
 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)
 {
 	spi_unregister_device(to_spi_device(dev));
@@ -2893,8 +2889,7 @@  void spi_unregister_controller(struct spi_controller *ctlr)
 	/* 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))
+	if (!ctlr->devm_allocated)
 		put_device(&ctlr->dev);
 
 	/* free bus id */
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 592897fa4f03..643139b1eafe 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -510,6 +510,9 @@  struct spi_controller {
 
 #define SPI_MASTER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */
 
+	/* flag indicating this is a non-devres managed controller */
+	bool			devm_allocated;
+
 	/* flag indicating this is an SPI slave controller */
 	bool			slave;