| From 88b3aee9a061d19ae9a3f5f3b27d0bc0437857be Mon Sep 17 00:00:00 2001 |
| From: Johan Hovold <johan@kernel.org> |
| Date: Wed, 9 Oct 2019 19:09:42 +0200 |
| Subject: [PATCH] USB: usb-skeleton: fix NULL-deref on disconnect |
| |
| 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> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c |
| index 85b01f1fcd80..e0cf11f798c5 100644 |
| --- a/drivers/usb/usb-skeleton.c |
| +++ b/drivers/usb/usb-skeleton.c |
| @@ -59,6 +59,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) |
| @@ -236,7 +237,7 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, |
| if (rv < 0) |
| return rv; |
| |
| - if (!dev->interface) { /* disconnect() was called */ |
| + if (dev->disconnected) { /* disconnect() was called */ |
| rv = -ENODEV; |
| goto exit; |
| } |
| @@ -418,7 +419,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, |
| |
| /* 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; |
| @@ -569,7 +570,7 @@ static void skel_disconnect(struct usb_interface *interface) |
| |
| /* 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); |
| -- |
| 2.7.4 |
| |