patch "USB: adutux: fix NULL-derefs on disconnect" added to usb-linus
Subsystems (2)
This is a note to let you know that I've just added the patch titled
USB: adutux: fix NULL-derefs on 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 b2fa7baee744fde746c17bc1860b9c6f5c2eebb7 Mon Sep 17 00:00:00 2001
From: Johan Hovold <[email protected]>
Date: Wed, 25 Sep 2019 11:29:13 +0200
Subject: USB: adutux: fix NULL-derefs on disconnect
The driver was using its struct usb_device pointer as an inverted disconnected flag, but was setting it to NULL before making sure all completion handlers had run. This could lead to a NULL-pointer dereference in a number of dev_dbg statements in the completion handlers which relies on said pointer.
The pointer was also dereferenced unconditionally in a dev_dbg statement release() something which would lead to a NULL-deref whenever a device was disconnected before the final character-device close if debugging was enabled.
Fix this by unconditionally stopping all I/O and preventing resubmissions by poisoning the interrupt URBs at disconnect and using a dedicated disconnected flag.
This also makes sure that all I/O has completed by the time the disconnect callback returns.
--- drivers/usb/misc/adutux.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/usb/misc/adutux.c +10 -6
@@ -75,6 +75,7 @@ struct adu_device {char serial_number[8 ];int open_count;/* number of times this port has been opened */ + unsigned long disconnected:1;char *read_buffer_primary;int read_buffer_length;
@@ -116,7 +117,7 @@ static void adu_abort_transfers(struct adu_device *dev) {unsigned long flags; -if (dev->udev ==NULL ) +if (dev->disconnected)return ;/* shutdown transfer */
@@ -243,7 +244,7 @@ static int adu_open(struct inode *inode, struct file *file) } dev =usb_get_intfdata (interface); -if (!dev || !dev->udev) { +if (!dev) { retval = -ENODEV ; goto exit_no_device; }
@@ -326,7 +327,7 @@ static int adu_release(struct inode *inode, struct file *file) }adu_release_internal (dev); -if (dev->udev ==NULL ) { +if (dev->disconnected) {/* the device was unplugged before the file was released */ if (!dev->open_count)/* ... and we're the last user */ adu_delete (dev);
@@ -354,7 +355,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,return -ERESTARTSYS ;/* verify that the device wasn't unplugged */ -if (dev->udev ==NULL ) { +if (dev->disconnected) { retval = -ENODEV ;pr_err ("No device or device unplugged %d\n" , retval); goto exit;
@@ -518,7 +519,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, goto exit_nolock;/* verify that the device wasn't unplugged */ -if (dev->udev ==NULL ) { +if (dev->disconnected) { retval = -ENODEV ;pr_err ("No device or device unplugged %d\n" , retval); goto exit;
@@ -764,11 +765,14 @@ static void adu_disconnect(struct usb_interface *interface)usb_deregister_dev (interface, &adu_class); +usb_poison_urb (dev->interrupt_in_urb); +usb_poison_urb (dev->interrupt_out_urb); +mutex_lock (&adutux_mutex);usb_set_intfdata (interface,NULL );mutex_lock (&dev->mtx);/* not interruptible */ - dev->udev =NULL ;/* poison */ + dev->disconnected =1 ;mutex_unlock (&dev->mtx);/* if the device is not opened, then we clean up right now */
Signature · 1 line
2.23.0