Skip to content

Commit a720416

Browse files
Bartosz Golaszewskibroonie
authored andcommitted
spi: spidev: fix a race condition when accessing spidev->spi
There's a spinlock in place that is taken in file_operations callbacks whenever we check if spidev->spi is still alive (not null). It's also taken when spidev->spi is set to NULL in remove(). This however doesn't protect the code against driver unbind event while one of the syscalls is still in progress. To that end we need a lock taken continuously as long as we may still access spidev->spi. As both the file ops and the remove callback are never called from interrupt context, we can replace the spinlock with a mutex. Signed-off-by: Bartosz Golaszewski <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent e8bb8f1 commit a720416

File tree

1 file changed

+18
-16
lines changed

1 file changed

+18
-16
lines changed

drivers/spi/spidev.c

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ static_assert(N_SPI_MINORS > 0 && N_SPI_MINORS <= 256);
6868

6969
struct spidev_data {
7070
dev_t devt;
71-
spinlock_t spi_lock;
71+
struct mutex spi_lock;
7272
struct spi_device *spi;
7373
struct list_head device_entry;
7474

@@ -95,9 +95,8 @@ spidev_sync(struct spidev_data *spidev, struct spi_message *message)
9595
int status;
9696
struct spi_device *spi;
9797

98-
spin_lock_irq(&spidev->spi_lock);
98+
mutex_lock(&spidev->spi_lock);
9999
spi = spidev->spi;
100-
spin_unlock_irq(&spidev->spi_lock);
101100

102101
if (spi == NULL)
103102
status = -ESHUTDOWN;
@@ -107,6 +106,7 @@ spidev_sync(struct spidev_data *spidev, struct spi_message *message)
107106
if (status == 0)
108107
status = message->actual_length;
109108

109+
mutex_unlock(&spidev->spi_lock);
110110
return status;
111111
}
112112

@@ -359,12 +359,12 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
359359
* we issue this ioctl.
360360
*/
361361
spidev = filp->private_data;
362-
spin_lock_irq(&spidev->spi_lock);
362+
mutex_lock(&spidev->spi_lock);
363363
spi = spi_dev_get(spidev->spi);
364-
spin_unlock_irq(&spidev->spi_lock);
365-
366-
if (spi == NULL)
364+
if (spi == NULL) {
365+
mutex_unlock(&spidev->spi_lock);
367366
return -ESHUTDOWN;
367+
}
368368

369369
/* use the buffer lock here for triple duty:
370370
* - prevent I/O (from us) so calling spi_setup() is safe;
@@ -508,6 +508,7 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
508508

509509
mutex_unlock(&spidev->buf_lock);
510510
spi_dev_put(spi);
511+
mutex_unlock(&spidev->spi_lock);
511512
return retval;
512513
}
513514

@@ -529,12 +530,12 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
529530
* we issue this ioctl.
530531
*/
531532
spidev = filp->private_data;
532-
spin_lock_irq(&spidev->spi_lock);
533+
mutex_lock(&spidev->spi_lock);
533534
spi = spi_dev_get(spidev->spi);
534-
spin_unlock_irq(&spidev->spi_lock);
535-
536-
if (spi == NULL)
535+
if (spi == NULL) {
536+
mutex_unlock(&spidev->spi_lock);
537537
return -ESHUTDOWN;
538+
}
538539

539540
/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
540541
mutex_lock(&spidev->buf_lock);
@@ -561,6 +562,7 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
561562
done:
562563
mutex_unlock(&spidev->buf_lock);
563564
spi_dev_put(spi);
565+
mutex_unlock(&spidev->spi_lock);
564566
return retval;
565567
}
566568

@@ -640,10 +642,10 @@ static int spidev_release(struct inode *inode, struct file *filp)
640642
spidev = filp->private_data;
641643
filp->private_data = NULL;
642644

643-
spin_lock_irq(&spidev->spi_lock);
645+
mutex_lock(&spidev->spi_lock);
644646
/* ... after we unbound from the underlying device? */
645647
dofree = (spidev->spi == NULL);
646-
spin_unlock_irq(&spidev->spi_lock);
648+
mutex_unlock(&spidev->spi_lock);
647649

648650
/* last close? */
649651
spidev->users--;
@@ -776,7 +778,7 @@ static int spidev_probe(struct spi_device *spi)
776778

777779
/* Initialize the driver data */
778780
spidev->spi = spi;
779-
spin_lock_init(&spidev->spi_lock);
781+
mutex_init(&spidev->spi_lock);
780782
mutex_init(&spidev->buf_lock);
781783

782784
INIT_LIST_HEAD(&spidev->device_entry);
@@ -821,9 +823,9 @@ static void spidev_remove(struct spi_device *spi)
821823
/* prevent new opens */
822824
mutex_lock(&device_list_lock);
823825
/* make sure ops on existing fds can abort cleanly */
824-
spin_lock_irq(&spidev->spi_lock);
826+
mutex_lock(&spidev->spi_lock);
825827
spidev->spi = NULL;
826-
spin_unlock_irq(&spidev->spi_lock);
828+
mutex_unlock(&spidev->spi_lock);
827829

828830
list_del(&spidev->device_entry);
829831
device_destroy(spidev_class, spidev->devt);

0 commit comments

Comments
 (0)