| From 726ed34eb3d6c7d0fdc13d38133d5a3ddd7f36ce Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 18 Jun 2020 11:21:24 +0800 |
| Subject: spi: spidev: fix a race between spidev_release and spidev_remove |
| |
| From: Zhenzhong Duan <zhenzhong.duan@gmail.com> |
| |
| [ Upstream commit abd42781c3d2155868821f1b947ae45bbc33330d ] |
| |
| 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> |
| Link: https://lore.kernel.org/r/20200618032125.4650-1-zhenzhong.duan@gmail.com |
| Signed-off-by: Mark Brown <broonie@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/spi/spidev.c | 4 ++-- |
| 1 file changed, 2 insertions(+), 2 deletions(-) |
| |
| diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c |
| index ab2c3848f5bf8..88d0976215fac 100644 |
| --- a/drivers/spi/spidev.c |
| +++ b/drivers/spi/spidev.c |
| @@ -783,13 +783,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); |
| -- |
| 2.25.1 |
| |