From 783f3b4e9ec50491c21746e7e05ec6c39c21f563 Mon Sep 17 00:00:00 2001 From: Bin Liu Date: Mon, 17 Sep 2018 11:40:22 -0500 Subject: [PATCH 01/10] usb: musb: dsps: do not disable CPPI41 irq in driver teardown TI AM335x CPPI 4.1 module uses a single register bit for CPPI interrupts in both musb controllers. So disabling the CPPI irq in one musb driver breaks the other musb module. Since musb is already disabled before tearing down dma controller in musb_remove(), it is safe to not disable CPPI irq in musb_dma_controller_destroy(). Fixes: 255348289f71 ("usb: musb: dsps: Manage CPPI 4.1 DMA interrupt in DSPS") Cc: stable@vger.kernel.org Signed-off-by: Bin Liu Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_dsps.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index df827ff57b0d0..23a0df79ef212 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -658,16 +658,6 @@ dsps_dma_controller_create(struct musb *musb, void __iomem *base) return controller; } -static void dsps_dma_controller_destroy(struct dma_controller *c) -{ - struct musb *musb = c->musb; - struct dsps_glue *glue = dev_get_drvdata(musb->controller->parent); - void __iomem *usbss_base = glue->usbss_base; - - musb_writel(usbss_base, USBSS_IRQ_CLEARR, USBSS_IRQ_PD_COMP); - cppi41_dma_controller_destroy(c); -} - #ifdef CONFIG_PM_SLEEP static void dsps_dma_controller_suspend(struct dsps_glue *glue) { @@ -697,7 +687,7 @@ static struct musb_platform_ops dsps_ops = { #ifdef CONFIG_USB_TI_CPPI41_DMA .dma_init = dsps_dma_controller_create, - .dma_exit = dsps_dma_controller_destroy, + .dma_exit = cppi41_dma_controller_destroy, #endif .enable = dsps_musb_enable, .disable = dsps_musb_disable, From e871db8d78df1c411032cbb3acfdf8930509360e Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 11 Sep 2018 10:00:44 +0200 Subject: [PATCH 02/10] Revert "usb: cdc-wdm: Fix a sleep-in-atomic-context bug in service_outstanding_interrupt()" This reverts commit 6e22e3af7bb3a7b9dc53cb4687659f6e63fca427. The bug the patch describes to, has been already fixed in commit 2df6948428542 ("USB: cdc-wdm: don't enable interrupts in USB-giveback") so need to this, revert it. Fixes: 6e22e3af7bb3 ("usb: cdc-wdm: Fix a sleep-in-atomic-context bug in service_outstanding_interrupt()") Cc: stable Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Greg Kroah-Hartman --- drivers/usb/class/cdc-wdm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index 656d247819c9d..bec581fb7c636 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -460,7 +460,7 @@ static int service_outstanding_interrupt(struct wdm_device *desc) set_bit(WDM_RESPONDING, &desc->flags); spin_unlock_irq(&desc->iuspin); - rv = usb_submit_urb(desc->response, GFP_ATOMIC); + rv = usb_submit_urb(desc->response, GFP_KERNEL); spin_lock_irq(&desc->iuspin); if (rv) { dev_err(&desc->intf->dev, From 7a68d9fb851012829c29e770621905529bd9490b Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 5 Sep 2018 12:07:02 +0200 Subject: [PATCH 03/10] USB: usbdevfs: sanitize flags more Requesting a ZERO_PACKET or not is sensible only for output. In the input direction the device decides. Likewise accepting short packets makes sense only for input. This allows operation with panic_on_warn without opening up a local DOS. Signed-off-by: Oliver Neukum Reported-by: syzbot+843efa30c8821bd69f53@syzkaller.appspotmail.com Fixes: 0cb54a3e47cb ("USB: debugging code shouldn't alter control flow") Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 6ce77b33da613..263dd2f309fb0 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1434,10 +1434,13 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb struct async *as = NULL; struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; - int i, ret, is_in, num_sgs = 0, ifnum = -1; + int i, ret, num_sgs = 0, ifnum = -1; int number_of_packets = 0; unsigned int stream_id = 0; void *buf; + bool is_in; + bool allow_short = false; + bool allow_zero = false; unsigned long mask = USBDEVFS_URB_SHORT_NOT_OK | USBDEVFS_URB_BULK_CONTINUATION | USBDEVFS_URB_NO_FSBR | @@ -1471,6 +1474,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u = 0; switch (uurb->type) { case USBDEVFS_URB_TYPE_CONTROL: + if (is_in) + allow_short = true; if (!usb_endpoint_xfer_control(&ep->desc)) return -EINVAL; /* min 8 byte setup packet */ @@ -1511,6 +1516,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb break; case USBDEVFS_URB_TYPE_BULK: + if (!is_in) + allow_zero = true; + else + allow_short = true; switch (usb_endpoint_type(&ep->desc)) { case USB_ENDPOINT_XFER_CONTROL: case USB_ENDPOINT_XFER_ISOC: @@ -1531,6 +1540,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb if (!usb_endpoint_xfer_int(&ep->desc)) return -EINVAL; interrupt_urb: + if (!is_in) + allow_zero = true; + else + allow_short = true; break; case USBDEVFS_URB_TYPE_ISO: @@ -1676,9 +1689,9 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u = (is_in ? URB_DIR_IN : URB_DIR_OUT); if (uurb->flags & USBDEVFS_URB_ISO_ASAP) u |= URB_ISO_ASAP; - if (uurb->flags & USBDEVFS_URB_SHORT_NOT_OK && is_in) + if (allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK) u |= URB_SHORT_NOT_OK; - if (uurb->flags & USBDEVFS_URB_ZERO_PACKET) + if (allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET) u |= URB_ZERO_PACKET; if (uurb->flags & USBDEVFS_URB_NO_INTERRUPT) u |= URB_NO_INTERRUPT; From 81e0403b26d94360abd1f6a57311337973bc82cd Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Wed, 5 Sep 2018 12:07:03 +0200 Subject: [PATCH 04/10] USB: usbdevfs: restore warning for nonsensical flags If we filter flags before they reach the core we need to generate our own warnings. Signed-off-by: Oliver Neukum Fixes: 0cb54a3e47cb ("USB: debugging code shouldn't alter control flow") Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 263dd2f309fb0..244417d0dfd1f 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -1697,6 +1697,11 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb u |= URB_NO_INTERRUPT; as->urb->transfer_flags = u; + if (!allow_short && uurb->flags & USBDEVFS_URB_SHORT_NOT_OK) + dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_SHORT_NOT_OK.\n"); + if (!allow_zero && uurb->flags & USBDEVFS_URB_ZERO_PACKET) + dev_warn(&ps->dev->dev, "Requested nonsensical USBDEVFS_URB_ZERO_PACKET.\n"); + as->urb->transfer_buffer_length = uurb->buffer_length; as->urb->setup_packet = (unsigned char *)dr; dr = NULL; From c183813fcee44a249339b7c46e1ad271ca1870aa Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 10 Sep 2018 13:58:51 -0400 Subject: [PATCH 05/10] USB: remove LPM management from usb_driver_claim_interface() usb_driver_claim_interface() disables and re-enables Link Power Management, but it shouldn't do either one, for the reasons listed below. This patch removes the two LPM-related function calls from the routine. The reason for disabling LPM in the analogous function usb_probe_interface() is so that drivers won't have to deal with unwanted LPM transitions in their probe routine. But usb_driver_claim_interface() doesn't call the driver's probe routine (or any other callbacks), so that reason doesn't apply here. Furthermore, no driver other than usbfs will ever call usb_driver_claim_interface() unless it is already bound to another interface in the same device, which means disabling LPM here would be redundant. usbfs doesn't interact with LPM at all. Lastly, the error return from usb_unlocked_disable_lpm() isn't handled properly; the code doesn't clean up its earlier actions before returning. Signed-off-by: Alan Stern Fixes: 8306095fd2c1 ("USB: Disable USB 3.0 LPM in critical sections.") CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index e76e95f62f762..7652dcb57998b 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -512,7 +512,6 @@ int usb_driver_claim_interface(struct usb_driver *driver, struct device *dev; struct usb_device *udev; int retval = 0; - int lpm_disable_error = -ENODEV; if (!iface) return -ENODEV; @@ -533,16 +532,6 @@ int usb_driver_claim_interface(struct usb_driver *driver, iface->condition = USB_INTERFACE_BOUND; - /* See the comment about disabling LPM in usb_probe_interface(). */ - if (driver->disable_hub_initiated_lpm) { - lpm_disable_error = usb_unlocked_disable_lpm(udev); - if (lpm_disable_error) { - dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n", - __func__, driver->name); - return -ENOMEM; - } - } - /* Claimed interfaces are initially inactive (suspended) and * runtime-PM-enabled, but only if the driver has autosuspend * support. Otherwise they are marked active, to prevent the @@ -561,10 +550,6 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (device_is_registered(dev)) retval = device_bind_driver(dev); - /* Attempt to re-enable USB3 LPM, if the disable was successful. */ - if (!lpm_disable_error) - usb_unlocked_enable_lpm(udev); - return retval; } EXPORT_SYMBOL_GPL(usb_driver_claim_interface); From bd729f9d67aa9a303d8925bb8c4f06af25f407d1 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 10 Sep 2018 13:59:59 -0400 Subject: [PATCH 06/10] USB: fix error handling in usb_driver_claim_interface() 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 Reported-by: syzbot+f84aa7209ccec829536f@syzkaller.appspotmail.com CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/driver.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 7652dcb57998b..a1f225f077cd2 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -550,6 +550,21 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (device_is_registered(dev)) retval = device_bind_driver(dev); + 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); From c9a4cb204e9eb7fa7dfbe3f7d3a674fa530aa193 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 10 Sep 2018 14:00:53 -0400 Subject: [PATCH 07/10] USB: handle NULL config in usb_find_alt_setting() usb_find_alt_setting() takes a pointer to a struct usb_host_config as an argument; it searches for an interface with specified interface and alternate setting numbers in that config. However, it crashes if the usb_host_config pointer argument is NULL. Since this is a general-purpose routine, available for use in many places, we want to to be more robust. This patch makes it return NULL whenever the config argument is NULL. Signed-off-by: Alan Stern Reported-by: syzbot+19c3aaef85a89d451eac@syzkaller.appspotmail.com CC: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/usb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 623be3174fb32..79d8bd7a612e6 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -228,6 +228,8 @@ struct usb_host_interface *usb_find_alt_setting( struct usb_interface_cache *intf_cache = NULL; int i; + if (!config) + return NULL; for (i = 0; i < config->desc.bNumInterfaces; i++) { if (config->intf_cache[i]->altsetting[0].desc.bInterfaceNumber == iface_num) { From 5c54fcac9a9de559b444ac63ec3cd82f1d157a0b Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 19 Sep 2018 10:58:05 +0300 Subject: [PATCH 08/10] usb: roles: Take care of driver module reference counting This fixes potential "BUG: unable to handle kernel paging request at ..." from happening. Fixes: fde0aa6c175a ("usb: common: Small class for USB role switches") Cc: Acked-by: Hans de Goede Tested-by: Hans de Goede Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/roles.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/usb/common/roles.c b/drivers/usb/common/roles.c index 15cc76e22123e..99116af07f1d9 100644 --- a/drivers/usb/common/roles.c +++ b/drivers/usb/common/roles.c @@ -109,8 +109,15 @@ static void *usb_role_switch_match(struct device_connection *con, int ep, */ struct usb_role_switch *usb_role_switch_get(struct device *dev) { - return device_connection_find_match(dev, "usb-role-switch", NULL, - usb_role_switch_match); + struct usb_role_switch *sw; + + sw = device_connection_find_match(dev, "usb-role-switch", NULL, + usb_role_switch_match); + + if (!IS_ERR_OR_NULL(sw)) + WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); + + return sw; } EXPORT_SYMBOL_GPL(usb_role_switch_get); @@ -122,8 +129,10 @@ EXPORT_SYMBOL_GPL(usb_role_switch_get); */ void usb_role_switch_put(struct usb_role_switch *sw) { - if (!IS_ERR_OR_NULL(sw)) + if (!IS_ERR_OR_NULL(sw)) { put_device(&sw->dev); + module_put(sw->dev.parent->driver->owner); + } } EXPORT_SYMBOL_GPL(usb_role_switch_put); From 16c4cb19fa85c648a803752eb63cac0ef69231c2 Mon Sep 17 00:00:00 2001 From: Harry Pan Date: Fri, 14 Sep 2018 16:58:16 +0800 Subject: [PATCH 09/10] usb: core: safely deal with the dynamic quirk lists Applying dynamic usbcore quirks in early booting when the slab is not yet ready would cause kernel panic of null pointer dereference because the quirk_count has been counted as 1 while the quirk_list was failed to allocate. i.e., [ 1.044970] BUG: unable to handle kernel NULL pointer dereference at (null) [ 1.044995] IP: [] usb_detect_quirks+0x88/0xd1 [ 1.045016] PGD 0 [ 1.045026] Oops: 0000 [#1] PREEMPT SMP [ 1.046986] gsmi: Log Shutdown Reason 0x03 [ 1.046995] Modules linked in: [ 1.047008] CPU: 0 PID: 81 Comm: kworker/0:3 Not tainted 4.4.154 #28 [ 1.047016] Hardware name: Google Coral/Coral, BIOS Google_Coral.10068.27.0 12/04/2017 [ 1.047028] Workqueue: usb_hub_wq hub_event [ 1.047037] task: ffff88017a321c80 task.stack: ffff88017a384000 [ 1.047044] RIP: 0010:[] [] usb_detect_quirks+0x88/0xd1 To tackle this odd, let's balance the quirk_count to 0 when the kcalloc call fails, and defer the quirk setting into a lower level callback which ensures that the kernel memory management has been initialized. Fixes: 027bd6cafd9a ("usb: core: Add "quirks" parameter for usbcore") Signed-off-by: Harry Pan Acked-by: Kai-Heng Feng Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/quirks.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index e77dfe5ed5ec7..178d6c6063c02 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -58,6 +58,7 @@ static int quirks_param_set(const char *val, const struct kernel_param *kp) quirk_list = kcalloc(quirk_count, sizeof(struct quirk_entry), GFP_KERNEL); if (!quirk_list) { + quirk_count = 0; mutex_unlock(&quirk_mutex); return -ENOMEM; } @@ -154,7 +155,7 @@ static struct kparam_string quirks_param_string = { .string = quirks_param, }; -module_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644); +device_param_cb(quirks, &quirks_param_ops, &quirks_param_string, 0644); MODULE_PARM_DESC(quirks, "Add/modify USB quirks by specifying quirks=vendorID:productID:quirks"); /* Lists of quirky USB devices, split in device quirks and interface quirks. From 3e3b81965cbfa01fda6d77750feedc3c46fc28d0 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 19 Sep 2018 10:58:04 +0300 Subject: [PATCH 10/10] usb: typec: mux: Take care of driver module reference counting Functions typec_mux_get() and typec_switch_get() already make sure that the mux device reference count is incremented, but the same must be done to the driver module as well to prevent the drivers from being unloaded in the middle of operation. This fixes a potential "BUG: unable to handle kernel paging request at ..." from happening. Fixes: 93dd2112c7b2 ("usb: typec: mux: Get the mux identifier from function parameter") Acked-by: Hans de Goede Tested-by: Hans de Goede Signed-off-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index ddaac63ecf122..d990aa510fabf 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -9,6 +9,7 @@ #include #include +#include #include #include @@ -49,8 +50,10 @@ struct typec_switch *typec_switch_get(struct device *dev) mutex_lock(&switch_lock); sw = device_connection_find_match(dev, "typec-switch", NULL, typec_switch_match); - if (!IS_ERR_OR_NULL(sw)) + if (!IS_ERR_OR_NULL(sw)) { + WARN_ON(!try_module_get(sw->dev->driver->owner)); get_device(sw->dev); + } mutex_unlock(&switch_lock); return sw; @@ -65,8 +68,10 @@ EXPORT_SYMBOL_GPL(typec_switch_get); */ void typec_switch_put(struct typec_switch *sw) { - if (!IS_ERR_OR_NULL(sw)) + if (!IS_ERR_OR_NULL(sw)) { + module_put(sw->dev->driver->owner); put_device(sw->dev); + } } EXPORT_SYMBOL_GPL(typec_switch_put); @@ -136,8 +141,10 @@ struct typec_mux *typec_mux_get(struct device *dev, const char *name) mutex_lock(&mux_lock); mux = device_connection_find_match(dev, name, NULL, typec_mux_match); - if (!IS_ERR_OR_NULL(mux)) + if (!IS_ERR_OR_NULL(mux)) { + WARN_ON(!try_module_get(mux->dev->driver->owner)); get_device(mux->dev); + } mutex_unlock(&mux_lock); return mux; @@ -152,8 +159,10 @@ EXPORT_SYMBOL_GPL(typec_mux_get); */ void typec_mux_put(struct typec_mux *mux) { - if (!IS_ERR_OR_NULL(mux)) + if (!IS_ERR_OR_NULL(mux)) { + module_put(mux->dev->driver->owner); put_device(mux->dev); + } } EXPORT_SYMBOL_GPL(typec_mux_put);