patch "USB: usblcd: fix I/O after disconnect" added to usb-linus
Subsystems (2)
This is a note to let you know that I've just added the patch titled
USB: usblcd: fix I/O after disconnect
to my usb git tree which can be found at
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
in the usb-linus branch.
The patch will show up in the next release of the linux-next tree (usually sometime within the next 24 hours during the week.)
The patch will hopefully also be merged in Linus's tree for the next -rc kernel release.
If you have any questions about this process, please let me know.
From eb7f5a490c5edfe8126f64bc58b9ba2edef0a425 Mon Sep 17 00:00:00 2001
From: Johan Hovold <[email protected]>
Date: Thu, 26 Sep 2019 11:12:25 +0200
Subject: USB: usblcd: fix I/O after disconnect
Make sure to stop all I/O on disconnect by adding a disconnected flag which is used to prevent new I/O from being started and by stopping all ongoing I/O before returning.
This also fixes a potential use-after-free on driver unbind in case the driver data is freed before the completion handler has run.
--- drivers/usb/misc/usblcd.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-)
drivers/usb/misc/usblcd.c +31 -2
@@ -18,6 +18,7 @@#include <linux/slab.h> #include <linux/errno.h> #include <linux/mutex.h> +#include <linux/rwsem.h> #include <linux/uaccess.h> #include <linux/usb.h>
@@ -57,6 +58,8 @@ struct usb_lcd {using up all RAM */ struct usb_anchor submitted;/* URBs to wait for before suspend */ +struct rw_semaphore io_rwsem; + unsigned long disconnected:1; };#define to_lcd_dev (d) container_of(d, struct usb_lcd, kref)
@@ -142,6 +145,13 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, dev = file->private_data; +down_read (&dev->io_rwsem); + +if (dev->disconnected) { + retval = -ENODEV ; + goto out_up_io; + } +/* do a blocking bulk read to get data from the device */ retval = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev,
@@ -158,6 +168,9 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, retval = bytes_read; } +out_up_io: +up_read (&dev->io_rwsem); +return retval; }
@@ -237,11 +250,18 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer,if (r <0 )return -EINTR ; +down_read (&dev->io_rwsem); + +if (dev->disconnected) { + retval = -ENODEV ; + goto err_up_io; + } +/* create a urb, and a buffer for it, and copy the data to the urb */ urb =usb_alloc_urb (0 ,GFP_KERNEL );if (!urb) { retval = -ENOMEM ; - goto err_no_buf; + goto err_up_io; } buf = usb_alloc_coherent(dev->udev, count, GFP_KERNEL,
@@ -278,6 +298,7 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer,the USB core will eventually free it entirely */ usb_free_urb (urb); +up_read (&dev->io_rwsem); exit:return count; error_unanchor:
@@ -285,7 +306,8 @@ static ssize_t lcd_write(struct file *file, const char __user * user_buffer, error:usb_free_coherent (dev->udev, count, buf, urb->transfer_dma);usb_free_urb (urb); -err_no_buf: +err_up_io: +up_read (&dev->io_rwsem);up (&dev->limit_sem);return retval; }
@@ -325,6 +347,7 @@ static int lcd_probe(struct usb_interface *interface,kref_init (&dev->kref);sema_init (&dev->limit_sem,USB_LCD_CONCURRENT_WRITES ); +init_rwsem (&dev->io_rwsem);init_usb_anchor (&dev->submitted); dev->udev =usb_get_dev (interface_to_usbdev (interface));
@@ -422,6 +445,12 @@ static void lcd_disconnect(struct usb_interface *interface)/* give back our minor */ usb_deregister_dev (interface, &lcd_class); +down_write (&dev->io_rwsem); + dev->disconnected =1 ; +up_write (&dev->io_rwsem); + +usb_kill_anchored_urbs (&dev->submitted); +/* decrement our usage count */ kref_put (&dev->kref, lcd_delete);
Signature · 1 line
2.23.0