summaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2018-09-10 13:59:59 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-10-03 16:59:19 -0700
commitf3a6a40b3793168f1f342aa7f2d97c82c97f24b6 (patch)
treec7975aa7ce0f229d66d4388daf213bf74d6a6c94 /drivers/usb
parent2c2860d0e8764e07c3016742eed8cb059c78d224 (diff)
downloadlinux-stable-f3a6a40b3793168f1f342aa7f2d97c82c97f24b6.tar.gz
linux-stable-f3a6a40b3793168f1f342aa7f2d97c82c97f24b6.tar.bz2
linux-stable-f3a6a40b3793168f1f342aa7f2d97c82c97f24b6.zip
USB: fix error handling in usb_driver_claim_interface()
commit bd729f9d67aa9a303d8925bb8c4f06af25f407d1 upstream. The syzbot fuzzing project found a use-after-free bug in the USB core. The bug was caused by usbfs not unbinding from an interface when the USB device file was closed, which led another process to attempt the unbind later on, after the private data structure had been deallocated. The reason usbfs did not unbind the interface at the appropriate time was because it thought the interface had never been claimed in the first place. This was caused by the fact that usb_driver_claim_interface() does not clean up properly when device_bind_driver() returns an error. Although the error code gets passed back to the caller, the iface->dev.driver pointer remains set and iface->condition remains equal to USB_INTERFACE_BOUND. This patch adds proper error handling to usb_driver_claim_interface(). Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: syzbot+f84aa7209ccec829536f@syzkaller.appspotmail.com CC: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/driver.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index e76e95f62f76..a5395498adda 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -565,6 +565,21 @@ int usb_driver_claim_interface(struct usb_driver *driver,
if (!lpm_disable_error)
usb_unlocked_enable_lpm(udev);
+ if (retval) {
+ dev->driver = NULL;
+ usb_set_intfdata(iface, NULL);
+ iface->needs_remote_wakeup = 0;
+ iface->condition = USB_INTERFACE_UNBOUND;
+
+ /*
+ * Unbound interfaces are always runtime-PM-disabled
+ * and runtime-PM-suspended
+ */
+ if (driver->supports_autosuspend)
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ }
+
return retval;
}
EXPORT_SYMBOL_GPL(usb_driver_claim_interface);