linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove
@ 2020-06-18  3:21 Zhenzhong Duan
  2020-06-18  3:21 ` [PATCH 2/2] spi: spidev: fix a potential use-after-free in spidev_release() Zhenzhong Duan
  2020-06-18 16:48 ` [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove Mark Brown
  0 siblings, 2 replies; 3+ messages in thread
From: Zhenzhong Duan @ 2020-06-18  3:21 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-spi, broonie, Zhenzhong Duan

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

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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH 2/2] spi: spidev: fix a potential use-after-free in spidev_release()
  2020-06-18  3:21 [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove Zhenzhong Duan
@ 2020-06-18  3:21 ` Zhenzhong Duan
  2020-06-18 16:48 ` [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove Mark Brown
  1 sibling, 0 replies; 3+ messages in thread
From: Zhenzhong Duan @ 2020-06-18  3:21 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-spi, broonie, Zhenzhong Duan

If an spi device is unbounded from the driver before the release
process, there will be an NULL pointer reference when it's
referenced in spi_slave_abort().

Fix it by checking it's already freed before reference.

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

diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index f74ea26..59e0767 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -609,15 +609,20 @@ static int spidev_open(struct inode *inode, struct file *filp)
 static int spidev_release(struct inode *inode, struct file *filp)
 {
 	struct spidev_data	*spidev;
+	int			dofree;
 
 	mutex_lock(&device_list_lock);
 	spidev = filp->private_data;
 	filp->private_data = NULL;
 
+	spin_lock_irq(&spidev->spi_lock);
+	/* ... after we unbound from the underlying device? */
+	dofree = (spidev->spi == NULL);
+	spin_unlock_irq(&spidev->spi_lock);
+
 	/* last close? */
 	spidev->users--;
 	if (!spidev->users) {
-		int		dofree;
 
 		kfree(spidev->tx_buffer);
 		spidev->tx_buffer = NULL;
@@ -625,19 +630,14 @@ static int spidev_release(struct inode *inode, struct file *filp)
 		kfree(spidev->rx_buffer);
 		spidev->rx_buffer = NULL;
 
-		spin_lock_irq(&spidev->spi_lock);
-		if (spidev->spi)
-			spidev->speed_hz = spidev->spi->max_speed_hz;
-
-		/* ... after we unbound from the underlying device? */
-		dofree = (spidev->spi == NULL);
-		spin_unlock_irq(&spidev->spi_lock);
-
 		if (dofree)
 			kfree(spidev);
+		else
+			spidev->speed_hz = spidev->spi->max_speed_hz;
 	}
 #ifdef CONFIG_SPI_SLAVE
-	spi_slave_abort(spidev->spi);
+	if (!dofree)
+		spi_slave_abort(spidev->spi);
 #endif
 	mutex_unlock(&device_list_lock);
 
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove
  2020-06-18  3:21 [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove Zhenzhong Duan
  2020-06-18  3:21 ` [PATCH 2/2] spi: spidev: fix a potential use-after-free in spidev_release() Zhenzhong Duan
@ 2020-06-18 16:48 ` Mark Brown
  1 sibling, 0 replies; 3+ messages in thread
From: Mark Brown @ 2020-06-18 16:48 UTC (permalink / raw)
  To: linux-kernel, Zhenzhong Duan; +Cc: linux-spi

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

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-06-18 16:48 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-18  3:21 [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove Zhenzhong Duan
2020-06-18  3:21 ` [PATCH 2/2] spi: spidev: fix a potential use-after-free in spidev_release() Zhenzhong Duan
2020-06-18 16:48 ` [PATCH 1/2] spi: spidev: fix a race between spidev_release and spidev_remove Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).