diff mbox series

[1/2] spi: spidev: fix a race between spidev_release and spidev_remove

Message ID 20200618032125.4650-1-zhenzhong.duan@gmail.com (mailing list archive)
State Accepted
Commit abd42781c3d2155868821f1b947ae45bbc33330d
Headers show
Series [1/2] spi: spidev: fix a race between spidev_release and spidev_remove | expand

Commit Message

Zhenzhong Duan June 18, 2020, 3:21 a.m. UTC
Imagine below scene, spidev is referenced after it's freed.

spidev_release()                spidev_remove()
...
                                spin_lock_irq(&spidev->spi_lock);
                                    spidev->spi = NULL;
                                spin_unlock_irq(&spidev->spi_lock);
mutex_lock(&device_list_lock);
dofree = (spidev->spi == NULL);
if (dofree)
    kfree(spidev);
mutex_unlock(&device_list_lock);
                                mutex_lock(&device_list_lock);
                                list_del(&spidev->device_entry);
                                device_destroy(spidev_class, spidev->devt);
                                clear_bit(MINOR(spidev->devt), minors);
                                if (spidev->users == 0)
                                    kfree(spidev);
                                mutex_unlock(&device_list_lock);

Fix it by resetting spidev->spi in device_list_lock's protection.

Signed-off-by: Zhenzhong Duan <zhenzhong.duan@gmail.com>
---
 drivers/spi/spidev.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Comments

Mark Brown June 18, 2020, 4:48 p.m. UTC | #1
On Thu, 18 Jun 2020 11:21:24 +0800, Zhenzhong Duan wrote:
> Imagine below scene, spidev is referenced after it's freed.
> 
> spidev_release()                spidev_remove()
> ...
>                                 spin_lock_irq(&spidev->spi_lock);
>                                     spidev->spi = NULL;
>                                 spin_unlock_irq(&spidev->spi_lock);
> mutex_lock(&device_list_lock);
> dofree = (spidev->spi == NULL);
> if (dofree)
>     kfree(spidev);
> mutex_unlock(&device_list_lock);
>                                 mutex_lock(&device_list_lock);
>                                 list_del(&spidev->device_entry);
>                                 device_destroy(spidev_class, spidev->devt);
>                                 clear_bit(MINOR(spidev->devt), minors);
>                                 if (spidev->users == 0)
>                                     kfree(spidev);
>                                 mutex_unlock(&device_list_lock);
> 
> [...]

Applied to

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

Thanks!

[1/2] spi: spidev: fix a race between spidev_release and spidev_remove
      commit: abd42781c3d2155868821f1b947ae45bbc33330d
[2/2] spi: spidev: fix a potential use-after-free in spidev_release()
      commit: 06096cc6c5a84ced929634b0d79376b94c65a4bd

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
diff mbox series

Patch

diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index d753df7..f74ea26 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -787,13 +787,13 @@  static int spidev_remove(struct spi_device *spi)
 {
 	struct spidev_data	*spidev = spi_get_drvdata(spi);
 
+	/* prevent new opens */
+	mutex_lock(&device_list_lock);
 	/* make sure ops on existing fds can abort cleanly */
 	spin_lock_irq(&spidev->spi_lock);
 	spidev->spi = NULL;
 	spin_unlock_irq(&spidev->spi_lock);
 
-	/* prevent new opens */
-	mutex_lock(&device_list_lock);
 	list_del(&spidev->device_entry);
 	device_destroy(spidev_class, spidev->devt);
 	clear_bit(MINOR(spidev->devt), minors);