From ff2e650873bfe2288a5b3c801dbc35ddb7064df1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 6 Jan 2023 11:07:18 +0100 Subject: [PATCH 1/2] spi: spidev: fix a race condition when accessing spidev->spi mainline inclusion from mainline-v6.3-rc1 commit 1f4d2dd45b6ef9c047f620d2812326a7813d2354 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAMULQ Reference: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git/commit/?h=main&id=1f4d2dd45b6e ---------------------------------------------------------------------- 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 Link: https://lore.kernel.org/r/20230106100719.196243-1-brgl@bgdev.pl Signed-off-by: Mark Brown Signed-off-by: huwentao --- drivers/spi/spidev.c | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index aee960a7d7f9..05b0585d5ced 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -67,7 +67,7 @@ static DECLARE_BITMAP(minors, N_SPI_MINORS); struct spidev_data { dev_t devt; - spinlock_t spi_lock; + struct mutex spi_lock; struct spi_device *spi; struct list_head device_entry; @@ -94,9 +94,8 @@ spidev_sync(struct spidev_data *spidev, struct spi_message *message) int status; struct spi_device *spi; - spin_lock_irq(&spidev->spi_lock); + mutex_lock(&spidev->spi_lock); spi = spidev->spi; - spin_unlock_irq(&spidev->spi_lock); if (spi == NULL) status = -ESHUTDOWN; @@ -106,6 +105,7 @@ spidev_sync(struct spidev_data *spidev, struct spi_message *message) if (status == 0) status = message->actual_length; + mutex_unlock(&spidev->spi_lock); return status; } @@ -358,12 +358,12 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) * we issue this ioctl. */ spidev = filp->private_data; - spin_lock_irq(&spidev->spi_lock); + mutex_lock(&spidev->spi_lock); spi = spi_dev_get(spidev->spi); - spin_unlock_irq(&spidev->spi_lock); - - if (spi == NULL) + if (spi == NULL) { + mutex_unlock(&spidev->spi_lock); return -ESHUTDOWN; + } /* use the buffer lock here for triple duty: * - prevent I/O (from us) so calling spi_setup() is safe; @@ -500,6 +500,7 @@ spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); + mutex_unlock(&spidev->spi_lock); return retval; } @@ -521,12 +522,12 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd, * we issue this ioctl. */ spidev = filp->private_data; - spin_lock_irq(&spidev->spi_lock); + mutex_lock(&spidev->spi_lock); spi = spi_dev_get(spidev->spi); - spin_unlock_irq(&spidev->spi_lock); - - if (spi == NULL) + if (spi == NULL) { + mutex_unlock(&spidev->spi_lock); return -ESHUTDOWN; + } /* SPI_IOC_MESSAGE needs the buffer locked "normally" */ mutex_lock(&spidev->buf_lock); @@ -553,6 +554,7 @@ spidev_compat_ioc_message(struct file *filp, unsigned int cmd, done: mutex_unlock(&spidev->buf_lock); spi_dev_put(spi); + mutex_unlock(&spidev->spi_lock); return retval; } @@ -629,10 +631,10 @@ static int spidev_release(struct inode *inode, struct file *filp) spidev = filp->private_data; filp->private_data = NULL; - spin_lock_irq(&spidev->spi_lock); + mutex_lock(&spidev->spi_lock); /* ... after we unbound from the underlying device? */ dofree = (spidev->spi == NULL); - spin_unlock_irq(&spidev->spi_lock); + mutex_unlock(&spidev->spi_lock); /* last close? */ spidev->users--; @@ -759,7 +761,7 @@ static int spidev_probe(struct spi_device *spi) /* Initialize the driver data */ spidev->spi = spi; - spin_lock_init(&spidev->spi_lock); + mutex_init(&spidev->spi_lock); mutex_init(&spidev->buf_lock); INIT_LIST_HEAD(&spidev->device_entry); @@ -804,9 +806,9 @@ static int spidev_remove(struct spi_device *spi) /* prevent new opens */ mutex_lock(&device_list_lock); /* make sure ops on existing fds can abort cleanly */ - spin_lock_irq(&spidev->spi_lock); + mutex_lock(&spidev->spi_lock); spidev->spi = NULL; - spin_unlock_irq(&spidev->spi_lock); + mutex_unlock(&spidev->spi_lock); list_del(&spidev->device_entry); device_destroy(spidev_class, spidev->devt); -- Gitee From da742a5432f72be53b9a16a7eb918215b8431dbd Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 16 Jan 2023 15:41:49 +0100 Subject: [PATCH 2/2] spi: spidev: fix a recursive locking error mainline inclusion from mainline-v6.2-rc8 commit eede42c9459b58b71edc99303dad65216a655810 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAMULQ Reference: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git/commit/?h=main&id=eede42c9459b ---------------------------------------------------------------------- When calling spidev_message() from the one of the ioctl() callbacks, the spi_lock is already taken. When we then end up calling spidev_sync(), we get the following splat: [ 214.047619] [ 214.049198] ============================================ [ 214.054533] WARNING: possible recursive locking detected [ 214.059858] 6.2.0-rc3-0.0.0-devel+git.97ec4d559d93 #1 Not tainted [ 214.065969] -------------------------------------------- [ 214.071290] spidev_test/1454 is trying to acquire lock: [ 214.076530] c4925dbc (&spidev->spi_lock){+.+.}-{3:3}, at: spidev_ioctl+0x8e0/0xab8 [ 214.084164] [ 214.084164] but task is already holding lock: [ 214.090007] c4925dbc (&spidev->spi_lock){+.+.}-{3:3}, at: spidev_ioctl+0x44/0xab8 [ 214.097537] [ 214.097537] other info that might help us debug this: [ 214.104075] Possible unsafe locking scenario: [ 214.104075] [ 214.110004] CPU0 [ 214.112461] ---- [ 214.114916] lock(&spidev->spi_lock); [ 214.118687] lock(&spidev->spi_lock); [ 214.122457] [ 214.122457] *** DEADLOCK *** [ 214.122457] [ 214.128386] May be due to missing lock nesting notation [ 214.128386] [ 214.135183] 2 locks held by spidev_test/1454: [ 214.139553] #0: c4925dbc (&spidev->spi_lock){+.+.}-{3:3}, at: spidev_ioctl+0x44/0xab8 [ 214.147524] #1: c4925e14 (&spidev->buf_lock){+.+.}-{3:3}, at: spidev_ioctl+0x70/0xab8 [ 214.155493] [ 214.155493] stack backtrace: [ 214.159861] CPU: 0 PID: 1454 Comm: spidev_test Not tainted 6.2.0-rc3-0.0.0-devel+git.97ec4d559d93 #1 [ 214.169012] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) [ 214.175555] unwind_backtrace from show_stack+0x10/0x14 [ 214.180819] show_stack from dump_stack_lvl+0x60/0x90 [ 214.185900] dump_stack_lvl from __lock_acquire+0x874/0x2858 [ 214.191584] __lock_acquire from lock_acquire+0xfc/0x378 [ 214.196918] lock_acquire from __mutex_lock+0x9c/0x8a8 [ 214.202083] __mutex_lock from mutex_lock_nested+0x1c/0x24 [ 214.207597] mutex_lock_nested from spidev_ioctl+0x8e0/0xab8 [ 214.213284] spidev_ioctl from sys_ioctl+0x4d0/0xe2c [ 214.218277] sys_ioctl from ret_fast_syscall+0x0/0x1c [ 214.223351] Exception stack(0xe75cdfa8 to 0xe75cdff0) [ 214.228422] dfa0: 00000000 00001000 00000003 40206b00 bee266e8 bee266e0 [ 214.236617] dfc0: 00000000 00001000 006a71a0 00000036 004c0040 004bfd18 00000000 00000003 [ 214.244809] dfe0: 00000036 bee266c8 b6f16dc5 b6e8e5f6 Fix it by introducing an unlocked variant of spidev_sync() and calling it from spidev_message() while other users who don't check the spidev->spi's existence keep on using the locking flavor. Reported-by: Francesco Dolcini Fixes: 1f4d2dd45b6e ("spi: spidev: fix a race condition when accessing spidev->spi") Signed-off-by: Bartosz Golaszewski Tested-by: Max Krummenacher Link: https://lore.kernel.org/r/20230116144149.305560-1-brgl@bgdev.pl Signed-off-by: Mark Brown Signed-off-by: huwentao --- drivers/spi/spidev.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 05b0585d5ced..1c072a6e947e 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -88,10 +88,22 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); /*-------------------------------------------------------------------------*/ +static ssize_t +spidev_sync_unlocked(struct spi_device *spi, struct spi_message *message) +{ + ssize_t status; + + status = spi_sync(spi, message); + if (status == 0) + status = message->actual_length; + + return status; +} + static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message) { - int status; + ssize_t status; struct spi_device *spi; mutex_lock(&spidev->spi_lock); @@ -100,12 +112,10 @@ spidev_sync(struct spidev_data *spidev, struct spi_message *message) if (spi == NULL) status = -ESHUTDOWN; else - status = spi_sync(spi, message); - - if (status == 0) - status = message->actual_length; + status = spidev_sync_unlocked(spi, message); mutex_unlock(&spidev->spi_lock); + return status; } @@ -293,7 +303,7 @@ static int spidev_message(struct spidev_data *spidev, spi_message_add_tail(k_tmp, &msg); } - status = spidev_sync(spidev, &msg); + status = spidev_sync_unlocked(spidev->spi, &msg); if (status < 0) goto done; -- Gitee