| From 9c4ca56e24b1747829a6d4522cfb9aba08f5820f Mon Sep 17 00:00:00 2001 |
| From: Johan Hovold <johan@kernel.org> |
| Date: Wed, 25 Sep 2019 11:29:13 +0200 |
| Subject: [PATCH] USB: adutux: fix NULL-derefs on disconnect |
| |
| commit b2fa7baee744fde746c17bc1860b9c6f5c2eebb7 upstream. |
| |
| 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. |
| |
| Fixes: 1ef37c6047fe ("USB: adutux: remove custom debug macro and module parameter") |
| Fixes: 66d4bc30d128 ("USB: adutux: remove custom debug macro") |
| Cc: stable <stable@vger.kernel.org> # 3.12 |
| Signed-off-by: Johan Hovold <johan@kernel.org> |
| Link: https://lore.kernel.org/r/20190925092913.8608-2-johan@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c |
| index fef855354ee4..aee542270e8b 100644 |
| --- a/drivers/usb/misc/adutux.c |
| +++ b/drivers/usb/misc/adutux.c |
| @@ -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); |
| @@ -355,7 +356,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; |
| @@ -520,7 +521,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; |
| @@ -766,11 +767,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 */ |
| -- |
| 2.7.4 |
| |