| From bed5ef230943863b9abf5eae226a20fad9a8ff71 Mon Sep 17 00:00:00 2001 |
| From: Johan Hovold <johan@kernel.org> |
| Date: Wed, 9 Oct 2019 19:09:42 +0200 |
| Subject: USB: usb-skeleton: fix NULL-deref on disconnect |
| |
| From: Johan Hovold <johan@kernel.org> |
| |
| commit bed5ef230943863b9abf5eae226a20fad9a8ff71 upstream. |
| |
| The driver was using its struct usb_interface pointer as an inverted |
| disconnected flag and was setting it to NULL before making sure all |
| completion handlers had run. This could lead to NULL-pointer |
| dereferences in the dev_err() statements in the completion handlers |
| which relies on said pointer. |
| |
| Fix this by using a dedicated disconnected flag. |
| |
| Note that this is also addresses a NULL-pointer dereference at release() |
| and a struct usb_interface reference leak introduced by a recent runtime |
| PM fix, which depends on and should have been submitted together with |
| this patch. |
| |
| Fixes: 4212cd74ca6f ("USB: usb-skeleton.c: remove err() usage") |
| Fixes: 5c290a5e42c3 ("USB: usb-skeleton: fix runtime PM after driver unbind") |
| Cc: stable <stable@vger.kernel.org> |
| Signed-off-by: Johan Hovold <johan@kernel.org> |
| Link: https://lore.kernel.org/r/20191009170944.30057-2-johan@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/usb-skeleton.c | 7 ++++--- |
| 1 file changed, 4 insertions(+), 3 deletions(-) |
| |
| --- a/drivers/usb/usb-skeleton.c |
| +++ b/drivers/usb/usb-skeleton.c |
| @@ -63,6 +63,7 @@ struct usb_skel { |
| spinlock_t err_lock; /* lock for errors */ |
| struct kref kref; |
| struct mutex io_mutex; /* synchronize I/O with disconnect */ |
| + unsigned long disconnected:1; |
| wait_queue_head_t bulk_in_wait; /* to wait for an ongoing read */ |
| }; |
| #define to_skel_dev(d) container_of(d, struct usb_skel, kref) |
| @@ -239,7 +240,7 @@ static ssize_t skel_read(struct file *fi |
| if (rv < 0) |
| return rv; |
| |
| - if (!dev->interface) { /* disconnect() was called */ |
| + if (dev->disconnected) { /* disconnect() was called */ |
| rv = -ENODEV; |
| goto exit; |
| } |
| @@ -420,7 +421,7 @@ static ssize_t skel_write(struct file *f |
| |
| /* this lock makes sure we don't submit URBs to gone devices */ |
| mutex_lock(&dev->io_mutex); |
| - if (!dev->interface) { /* disconnect() was called */ |
| + if (dev->disconnected) { /* disconnect() was called */ |
| mutex_unlock(&dev->io_mutex); |
| retval = -ENODEV; |
| goto error; |
| @@ -588,7 +589,7 @@ static void skel_disconnect(struct usb_i |
| |
| /* prevent more I/O from starting */ |
| mutex_lock(&dev->io_mutex); |
| - dev->interface = NULL; |
| + dev->disconnected = 1; |
| mutex_unlock(&dev->io_mutex); |
| |
| usb_kill_anchored_urbs(&dev->submitted); |