spi: spidev: fix a potential use-after-free in spidev_release()

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>
Link: https://lore.kernel.org/r/20200618032125.4650-2-zhenzhong.duan@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Zhenzhong Duan 2020-06-18 11:21:25 +08:00 committed by Mark Brown
parent abd42781c3
commit 06096cc6c5
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0

View File

@ -609,15 +609,20 @@ err_find_dev:
static int spidev_release(struct inode *inode, struct file *filp) static int spidev_release(struct inode *inode, struct file *filp)
{ {
struct spidev_data *spidev; struct spidev_data *spidev;
int dofree;
mutex_lock(&device_list_lock); mutex_lock(&device_list_lock);
spidev = filp->private_data; spidev = filp->private_data;
filp->private_data = NULL; 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? */ /* last close? */
spidev->users--; spidev->users--;
if (!spidev->users) { if (!spidev->users) {
int dofree;
kfree(spidev->tx_buffer); kfree(spidev->tx_buffer);
spidev->tx_buffer = NULL; spidev->tx_buffer = NULL;
@ -625,19 +630,14 @@ static int spidev_release(struct inode *inode, struct file *filp)
kfree(spidev->rx_buffer); kfree(spidev->rx_buffer);
spidev->rx_buffer = NULL; 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) if (dofree)
kfree(spidev); kfree(spidev);
else
spidev->speed_hz = spidev->spi->max_speed_hz;
} }
#ifdef CONFIG_SPI_SLAVE #ifdef CONFIG_SPI_SLAVE
spi_slave_abort(spidev->spi); if (!dofree)
spi_slave_abort(spidev->spi);
#endif #endif
mutex_unlock(&device_list_lock); mutex_unlock(&device_list_lock);