diff options
Diffstat (limited to 'drivers')
41 files changed, 438 insertions, 201 deletions
diff --git a/drivers/thunderbolt/property.c b/drivers/thunderbolt/property.c index 50cbfc92fe65..da2c59a17db5 100644 --- a/drivers/thunderbolt/property.c +++ b/drivers/thunderbolt/property.c @@ -8,6 +8,7 @@ */ #include <linux/err.h> +#include <linux/overflow.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/uuid.h> @@ -34,10 +35,11 @@ struct tb_property_dir_entry { }; #define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401 +#define TB_PROPERTY_MAX_DEPTH 8 static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, size_t block_len, unsigned int dir_offset, size_t dir_len, - bool is_root); + bool is_root, unsigned int depth); static inline void parse_dwdata(void *dst, const void *src, size_t dwords) { @@ -52,13 +54,16 @@ static inline void format_dwdata(void *dst, const void *src, size_t dwords) static bool tb_property_entry_valid(const struct tb_property_entry *entry, size_t block_len) { + u32 end; + switch (entry->type) { case TB_PROPERTY_TYPE_DIRECTORY: case TB_PROPERTY_TYPE_DATA: case TB_PROPERTY_TYPE_TEXT: if (entry->length > block_len) return false; - if (entry->value + entry->length > block_len) + if (check_add_overflow(entry->value, entry->length, &end) || + end > block_len) return false; break; @@ -93,7 +98,8 @@ tb_property_alloc(const char *key, enum tb_property_type type) } static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, - const struct tb_property_entry *entry) + const struct tb_property_entry *entry, + unsigned int depth) { char key[TB_PROPERTY_KEY_SIZE + 1]; struct tb_property *property; @@ -114,7 +120,7 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, switch (property->type) { case TB_PROPERTY_TYPE_DIRECTORY: dir = __tb_property_parse_dir(block, block_len, entry->value, - entry->length, false); + entry->length, false, depth + 1); if (!dir) { kfree(property); return NULL; @@ -159,21 +165,31 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len, } static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, - size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root) + size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root, + unsigned int depth) { const struct tb_property_entry *entries; size_t i, content_len, nentries; unsigned int content_offset; struct tb_property_dir *dir; + if (depth > TB_PROPERTY_MAX_DEPTH) + return NULL; + dir = kzalloc_obj(*dir); if (!dir) return NULL; + INIT_LIST_HEAD(&dir->properties); + if (is_root) { content_offset = dir_offset + 2; content_len = dir_len; } else { + if (dir_len < 4) { + tb_property_free_dir(dir); + return NULL; + } dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid), GFP_KERNEL); if (!dir->uuid) { @@ -187,12 +203,10 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block, entries = (const struct tb_property_entry *)&block[content_offset]; nentries = content_len / (sizeof(*entries) / 4); - INIT_LIST_HEAD(&dir->properties); - for (i = 0; i < nentries; i++) { struct tb_property *property; - property = tb_property_parse(block, block_len, &entries[i]); + property = tb_property_parse(block, block_len, &entries[i], depth); if (!property) { tb_property_free_dir(dir); return NULL; @@ -231,7 +245,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block, return NULL; return __tb_property_parse_dir(block, block_len, 0, rootdir->length, - true); + true, 0); } /** diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 8382231af357..1db8db1b7cc3 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -2817,9 +2817,19 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep) priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING); if (request) { - if (trb) + if (trb) { *trb = trb_tmp; + /* + * Per datasheet, EPRST causes DMA to reposition to the next TD. + * Manually reset EP_TRADDR to the current TRB to prevent + * the hardware from skipping the interrupted request. + */ + writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma + + priv_req->start_trb * TRB_SIZE), + &priv_dev->regs->ep_traddr); + } + cdns3_rearm_transfer(priv_ep, 1); } diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 735df88774e4..94e9706a1806 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -126,15 +126,15 @@ static int cdns3_plat_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy), "Failed to get cdn3,usb2-phy\n"); - ret = phy_init(cdns->usb2_phy); - if (ret) - return ret; - cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy"); if (IS_ERR(cdns->usb3_phy)) return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy), "Failed to get cdn3,usb3-phy\n"); + ret = phy_init(cdns->usb2_phy); + if (ret) + return ret; + ret = phy_init(cdns->usb3_phy); if (ret) goto err_phy3_init; @@ -186,6 +186,9 @@ static void cdns3_plat_remove(struct platform_device *pdev) struct device *dev = cdns->dev; pm_runtime_get_sync(dev); + if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW))) + pm_runtime_allow(dev); + pm_runtime_disable(dev); pm_runtime_put_noidle(dev); cdns_remove(cdns); diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 7cfabb04a4fb..2ab3db3c1015 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -655,12 +655,6 @@ static enum ci_role ci_get_role(struct ci_hdrc *ci) return role; } -static struct usb_role_switch_desc ci_role_switch = { - .set = ci_usb_role_switch_set, - .get = ci_usb_role_switch_get, - .allow_userspace_control = true, -}; - static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { @@ -787,9 +781,6 @@ static int ci_get_platdata(struct device *dev, cable->connected = false; } - if (device_property_read_bool(dev, "usb-role-switch")) - ci_role_switch.fwnode = dev->fwnode; - platdata->pctl = devm_pinctrl_get(dev); if (!IS_ERR(platdata->pctl)) { struct pinctrl_state *p; @@ -1033,6 +1024,7 @@ ATTRIBUTE_GROUPS(ci); static int ci_hdrc_probe(struct platform_device *pdev) { + struct usb_role_switch_desc ci_role_switch = {}; struct device *dev = &pdev->dev; struct ci_hdrc *ci; struct resource *res; @@ -1179,7 +1171,11 @@ static int ci_hdrc_probe(struct platform_device *pdev) } } - if (ci_role_switch.fwnode) { + if (device_property_read_bool(dev, "usb-role-switch")) { + ci_role_switch.set = ci_usb_role_switch_set; + ci_role_switch.get = ci_usb_role_switch_get; + ci_role_switch.allow_userspace_control = true; + ci_role_switch.fwnode = dev_fwnode(dev); ci_role_switch.driver_data = ci; ci->role_switch = usb_role_switch_register(dev, &ci_role_switch); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 54059e4fc6ed..ddf0b5963859 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -114,8 +114,6 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, int retval; retval = usb_autopm_get_interface(acm->control); -#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */ -#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */ if (retval) return retval; diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 25fd5329a878..01f448a783c0 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -115,3 +115,5 @@ struct acm { #define DISABLE_ECHO BIT(7) #define MISSING_CAP_BRK BIT(8) #define NO_UNION_12 BIT(9) +#define VENDOR_CLASS_DATA_IFACE BIT(10) /* data interface uses vendor-specific class */ +#define ALWAYS_POLL_CTRL BIT(11) /* keep ctrl URB active even without an open TTY */ diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index bd9347804dec..af9ae55dae14 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -2306,6 +2306,14 @@ static void usbtmc_interrupt(struct urb *urb) switch (status) { case 0: /* SUCCESS */ + /* ensure at least two bytes of headers were transferred */ + if (urb->actual_length < 2) { + dev_warn(dev, + "actual length %d not sufficient for interrupt headers\n", + urb->actual_length); + goto exit; + } + /* check for valid STB notification */ if (data->iin_buffer[0] > 0x81) { data->bNotify1 = data->iin_buffer[0]; @@ -2432,6 +2440,12 @@ static int usbtmc_probe(struct usb_interface *intf, data->iin_ep = int_in->bEndpointAddress; data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in); data->iin_interval = int_in->bInterval; + /* wMaxPacketSize should be 0x02 or more as per USB488 Table 22 */ + if (iface_desc->desc.bInterfaceProtocol == 1 && + data->iin_wMaxPacketSize < 2) { + retcode = -EINVAL; + goto err_put; + } dev_dbg(&intf->dev, "Found Int in endpoint at %u\n", data->iin_ep); } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 417140b012bb..45e20c6d76c0 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -56,8 +56,7 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev, desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer; if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE || desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) { - dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion" - "for config %d interface %d altsetting %d ep %d.\n", + dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion for config %d interface %d altsetting %d ep 0x%X.\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); return; } @@ -91,7 +90,7 @@ static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev, size -= h->bLength; } - dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n", + dev_notice(ddev, "No eUSB2 isoc ep 0x%X companion for config %d interface %d altsetting %d\n", ep->desc.bEndpointAddress, cfgno, inum, asnum); } @@ -115,9 +114,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, } if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) { - dev_notice(ddev, "No SuperSpeed endpoint companion for config %d " - " interface %d altsetting %d ep %d: " - "using minimum values\n", + dev_notice(ddev, "No SuperSpeed endpoint companion for config %d interface %d altsetting %d ep 0x%X: using minimum values\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); /* Fill in some default values. @@ -141,42 +138,32 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, /* Check the various values */ if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) { - dev_notice(ddev, "Control endpoint with bMaxBurst = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to zero\n", desc->bMaxBurst, - cfgno, inum, asnum, ep->desc.bEndpointAddress); + dev_notice(ddev, "Control endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", + desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bMaxBurst = 0; } else if (desc->bMaxBurst > 15) { - dev_notice(ddev, "Endpoint with bMaxBurst = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to 15\n", desc->bMaxBurst, - cfgno, inum, asnum, ep->desc.bEndpointAddress); + dev_notice(ddev, "Endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to 15\n", + desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bMaxBurst = 15; } if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) && desc->bmAttributes != 0) { - dev_notice(ddev, "%s endpoint with bmAttributes = %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to zero\n", + dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n", usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk", desc->bmAttributes, cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 0; } else if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) { - dev_notice(ddev, "Bulk endpoint with more than 65536 streams in " - "config %d interface %d altsetting %d ep %d: " - "setting to max\n", + dev_notice(ddev, "Bulk endpoint with more than 65536 streams in config %d interface %d altsetting %d ep 0x%X: setting to max\n", cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 16; } else if (usb_endpoint_xfer_isoc(&ep->desc) && !USB_SS_SSP_ISOC_COMP(desc->bmAttributes) && USB_SS_MULT(desc->bmAttributes) > 3) { - dev_notice(ddev, "Isoc endpoint has Mult of %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to 3\n", + dev_notice(ddev, "Isoc endpoint has Mult of %d in config %d interface %d altsetting %d ep 0x%X: setting to 3\n", USB_SS_MULT(desc->bmAttributes), cfgno, inum, asnum, ep->desc.bEndpointAddress); ep->ss_ep_comp.bmAttributes = 2; @@ -191,10 +178,15 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, (desc->bMaxBurst + 1); else max_tx = 999999; - if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) { - dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in " - "config %d interface %d altsetting %d ep %d: " - "setting to %d\n", + /* + * wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite. + * Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN + * endpoints is usually bogus too, and recent HCs enforce interrupt BW limits. + */ + if (le16_to_cpu(desc->wBytesPerInterval) > max_tx || + (le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) && + usb_endpoint_is_int_in(&ep->desc))) { + dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in config %d interface %d altsetting %d ep 0x%X: setting to %d\n", usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int", le16_to_cpu(desc->wBytesPerInterval), cfgno, inum, asnum, ep->desc.bEndpointAddress, diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 89221f1ce769..b181b43a35dc 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -328,9 +328,7 @@ static const u8 ss_rh_config_descriptor[] = { USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ - /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) - * see hub.c:hub_configure() for details. */ - (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, + 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 2 bytes per USB3 10.15.1 */ 0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ /* one SuperSpeed endpoint companion descriptor */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 0ffdaefba508..87810eff974e 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -513,6 +513,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */ { USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM }, + /* Lenovo ThinkPad USB-C Dock Gen2 USB 3.1 and USB 2.0 hub controllers */ + { USB_DEVICE(0x17ef, 0xa391), .driver_info = USB_QUIRK_NO_LPM }, + { USB_DEVICE(0x17ef, 0xa392), .driver_info = USB_QUIRK_NO_LPM }, + /* BUILDWIN Photo Frame */ { USB_DEVICE(0x1908, 0x1315), .driver_info = USB_QUIRK_HONOR_BNUMINTERFACES }, diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 1a763ad4f721..2414291aa908 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -4804,6 +4804,7 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); int rc; unsigned long flags; + int urb_status; dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n"); dwc2_dump_urb_info(hcd, urb, "urb_dequeue"); @@ -4828,11 +4829,12 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, /* Higher layer software sets URB status */ spin_unlock(&hsotg->lock); + urb_status = urb->status; usb_hcd_giveback_urb(hcd, urb, status); spin_lock(&hsotg->lock); dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n"); - dev_dbg(hsotg->dev, " urb->status = %d\n", urb->status); + dev_dbg(hsotg->dev, " urb->status = %d\n", urb_status); out: spin_unlock_irqrestore(&hsotg->lock, flags); diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index f41b0da5e89d..9b9525592a85 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -184,15 +184,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } ret = phy_init(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); + if (ret < 0) goto err; - } ret = reset_control_deassert(apbrst); if (ret < 0) { dev_err(dev, "Failed to release APB reset\n"); - goto err; + goto err_phy_exit; } if (priv_data->usb3_phy) { @@ -208,26 +206,24 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) ret = reset_control_deassert(crst); if (ret < 0) { dev_err(dev, "Failed to release core reset\n"); - goto err; + goto err_phy_exit; } ret = reset_control_deassert(hibrst); if (ret < 0) { dev_err(dev, "Failed to release hibernation reset\n"); - goto err; + goto err_phy_exit; } ret = phy_power_on(priv_data->usb3_phy); - if (ret < 0) { - phy_exit(priv_data->usb3_phy); - goto err; - } + if (ret < 0) + goto err_phy_exit; /* ulpi reset via gpio-modepin or gpio-framework driver */ reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(reset_gpio)) { - return dev_err_probe(dev, PTR_ERR(reset_gpio), - "Failed to request reset GPIO\n"); + ret = PTR_ERR(reset_gpio); + goto err_phy_power_off; } if (reset_gpio) { @@ -237,6 +233,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data) } dwc3_xlnx_set_coherency(priv_data, XLNX_USB_TRAFFIC_ROUTE_CONFIG); + + return 0; + +err_phy_power_off: + phy_power_off(priv_data->usb3_phy); +err_phy_exit: + phy_exit(priv_data->usb3_phy); err: return ret; } diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index a902184bdf82..dc3664374596 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -2172,7 +2172,10 @@ unknown: sizeof(url_descriptor->URL) - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset); - if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) + if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH) + landing_page_length = landing_page_offset; + else if (w_length < + WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) landing_page_length = w_length - WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 002c3441bea3..75912ce6ab55 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -150,6 +150,8 @@ struct ffs_dma_fence { struct dma_fence base; struct ffs_dmabuf_priv *priv; struct work_struct work; + struct usb_ep *ep; + struct usb_request *req; }; struct ffs_epfile { @@ -619,7 +621,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf, /* unlocks spinlock */ ret = __ffs_ep0_queue_wait(ffs, data, len); - if ((ret > 0) && (copy_to_user(buf, data, len))) + if ((ret > 0) && (copy_to_user(buf, data, ret))) ret = -EFAULT; goto done_mutex; @@ -1385,6 +1387,21 @@ static void ffs_dmabuf_cleanup(struct work_struct *work) struct ffs_dmabuf_priv *priv = dma_fence->priv; struct dma_buf_attachment *attach = priv->attach; struct dma_fence *fence = &dma_fence->base; + struct usb_request *req = dma_fence->req; + struct usb_ep *ep = dma_fence->ep; + + /* + * eps_lock pairs with the cancel paths so they cannot pass a freed + * req to usb_ep_dequeue(). Only clear if priv->req still names ours; + * a re-queue on the same attachment may have taken that slot. + */ + spin_lock_irq(&priv->ffs->eps_lock); + if (priv->req == req) + priv->req = NULL; + spin_unlock_irq(&priv->ffs->eps_lock); + + if (ep && req) + usb_ep_free_request(ep, req); ffs_dmabuf_put(attach); dma_fence_put(fence); @@ -1414,8 +1431,8 @@ static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep, struct usb_request *req) { pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status); + /* req is freed by ffs_dmabuf_cleanup() under eps_lock. */ ffs_dmabuf_signal_done(req->context, req->status); - usb_ep_free_request(ep, req); } static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence) @@ -1699,6 +1716,10 @@ static int ffs_dmabuf_transfer(struct file *file, usb_req->context = fence; usb_req->complete = ffs_epfile_dmabuf_io_complete; + /* ffs_dmabuf_cleanup() frees usb_req via these two fields. */ + fence->req = usb_req; + fence->ep = ep->ep; + cookie = dma_fence_begin_signalling(); ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC); dma_fence_end_signalling(cookie); @@ -1708,7 +1729,6 @@ static int ffs_dmabuf_transfer(struct file *file, } else { pr_warn("FFS: Failed to queue DMABUF: %d\n", ret); ffs_dmabuf_signal_done(fence, ret); - usb_ep_free_request(ep->ep, usb_req); } spin_unlock_irq(&epfile->ffs->eps_lock); diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index c5a12a6760ea..3c6b43d06a6d 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1622,7 +1622,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) hidg->dev.devt = MKDEV(major, opts->minor); ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor); if (ret) - goto err_unlock; + goto err_put_device; hidg->bInterfaceSubClass = opts->subclass; hidg->bInterfaceProtocol = opts->protocol; @@ -1659,7 +1659,6 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) err_put_device: put_device(&hidg->dev); -err_unlock: mutex_unlock(&opts->lock); return ERR_PTR(ret); } diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 8d404d88391c..73dc7e42875f 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -769,6 +769,16 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address; /* + * Hold opts->lock across both the XU string-descriptor fixup below and + * the descriptor-copy block further down. Without this, configfs + * uvcg_extension_drop() (which takes opts->lock) can race with the + * list_for_each_entry() walks here and inside uvc_copy_descriptors(), + * leading to a UAF on a freed struct uvcg_extension. See + * drivers/usb/gadget/function/uvc_configfs.c::uvcg_extension_drop(). + */ + mutex_lock(&opts->lock); + + /* * XUs can have an arbitrary string descriptor describing them. If they * have one pick up the ID. */ @@ -785,7 +795,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) ARRAY_SIZE(uvc_en_us_strings)); if (IS_ERR(us)) { ret = PTR_ERR(us); - goto error; + goto error_unlock; } uvc_iad.iFunction = opts->iad_index ? cdev->usb_strings[opts->iad_index].id : @@ -799,14 +809,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) /* Allocate interface IDs. */ if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_iad.bFirstInterface = ret; uvc_control_intf.bInterfaceNumber = ret; uvc->control_intf = ret; opts->control_interface = ret; if ((ret = usb_interface_id(c, f)) < 0) - goto error; + goto error_unlock; uvc_streaming_intf_alt0.bInterfaceNumber = ret; uvc_streaming_intf_alt1.bInterfaceNumber = ret; uvc->streaming_intf = ret; @@ -817,30 +827,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) if (IS_ERR(f->fs_descriptors)) { ret = PTR_ERR(f->fs_descriptors); f->fs_descriptors = NULL; - goto error; + goto error_unlock; } f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH); if (IS_ERR(f->hs_descriptors)) { ret = PTR_ERR(f->hs_descriptors); f->hs_descriptors = NULL; - goto error; + goto error_unlock; } f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER); if (IS_ERR(f->ss_descriptors)) { ret = PTR_ERR(f->ss_descriptors); f->ss_descriptors = NULL; - goto error; + goto error_unlock; } f->ssp_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER_PLUS); if (IS_ERR(f->ssp_descriptors)) { ret = PTR_ERR(f->ssp_descriptors); f->ssp_descriptors = NULL; - goto error; + goto error_unlock; } + mutex_unlock(&opts->lock); + /* Preallocate control endpoint request. */ uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL); uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL); @@ -872,6 +884,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) return 0; +error_unlock: + mutex_unlock(&opts->lock); v4l2_error: v4l2_device_unregister(&uvc->v4l2_dev); error: diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index f094491b1041..f47903461ed5 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -2134,6 +2134,8 @@ static int dummy_hub_control( case ClearHubFeature: break; case ClearPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_SUSPEND: if (hcd->speed == HCD_USB3) { @@ -2248,6 +2250,8 @@ static int dummy_hub_control( retval = -EPIPE; break; case SetPortFeature: + if (wIndex != 1) + goto error; switch (wValue) { case USB_PORT_FEAT_LINK_STATE: if (hcd->speed != HCD_USB3) { diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index d02765bd49ce..7c5f30cfd24d 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -3790,10 +3790,8 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; done: - if (dev) { + if (dev) net2280_remove(pdev); - kfree(dev); - } return retval; } diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index d2214d309e96..d5637b376367 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -247,6 +247,7 @@ struct tegra_xusb_soc { bool has_ipfs; bool lpm_support; bool otg_reset_sspi; + bool otg_set_port_power; bool has_bar2; }; @@ -1352,12 +1353,13 @@ static void tegra_xhci_id_work(struct work_struct *work) struct tegra_xusb_mbox_msg msg; struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", tegra->otg_usb2_port); + bool host_mode = tegra->host_mode; u32 status; int ret; - dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode)); + dev_dbg(tegra->dev, "host mode %s\n", str_on_off(host_mode)); - if (tegra->host_mode) + if (host_mode) phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST); else phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE); @@ -1366,41 +1368,43 @@ static void tegra_xhci_id_work(struct work_struct *work) tegra->otg_usb2_port); pm_runtime_get_sync(tegra->dev); - if (tegra->host_mode) { - /* switch to host mode */ - if (tegra->otg_usb3_port >= 0) { - if (tegra->soc->otg_reset_sspi) { - /* set PP=0 */ - tegra_xhci_hc_driver.hub_control( - xhci->shared_hcd, GetPortStatus, - 0, tegra->otg_usb3_port+1, - (char *) &status, sizeof(status)); - if (status & USB_SS_PORT_STAT_POWER) - tegra_xhci_set_port_power(tegra, false, - false); - - /* reset OTG port SSPI */ - msg.cmd = MBOX_CMD_RESET_SSPI; - msg.data = tegra->otg_usb3_port+1; - - ret = tegra_xusb_mbox_send(tegra, &msg); - if (ret < 0) { - dev_info(tegra->dev, - "failed to RESET_SSPI %d\n", - ret); + if (tegra->soc->otg_set_port_power) { + if (host_mode) { + /* switch to host mode */ + if (tegra->otg_usb3_port >= 0) { + if (tegra->soc->otg_reset_sspi) { + /* set PP=0 */ + tegra_xhci_hc_driver.hub_control( + xhci->shared_hcd, GetPortStatus, + 0, tegra->otg_usb3_port+1, + (char *) &status, sizeof(status)); + if (status & USB_SS_PORT_STAT_POWER) + tegra_xhci_set_port_power(tegra, false, + false); + + /* reset OTG port SSPI */ + msg.cmd = MBOX_CMD_RESET_SSPI; + msg.data = tegra->otg_usb3_port+1; + + ret = tegra_xusb_mbox_send(tegra, &msg); + if (ret < 0) { + dev_info(tegra->dev, + "failed to RESET_SSPI %d\n", + ret); + } } - } - tegra_xhci_set_port_power(tegra, false, true); - } + tegra_xhci_set_port_power(tegra, false, true); + } - tegra_xhci_set_port_power(tegra, true, true); + tegra_xhci_set_port_power(tegra, true, true); - } else { - if (tegra->otg_usb3_port >= 0) - tegra_xhci_set_port_power(tegra, false, false); + } else { + if (tegra->otg_usb3_port >= 0) + tegra_xhci_set_port_power(tegra, false, false); - tegra_xhci_set_port_power(tegra, true, false); + tegra_xhci_set_port_power(tegra, true, false); + } } pm_runtime_put_autosuspend(tegra->dev); } @@ -2553,6 +2557,7 @@ static const struct tegra_xusb_soc tegra124_soc = { .scale_ss_clock = true, .has_ipfs = true, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2593,6 +2598,7 @@ static const struct tegra_xusb_soc tegra210_soc = { .scale_ss_clock = false, .has_ipfs = true, .otg_reset_sspi = true, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2640,6 +2646,7 @@ static const struct tegra_xusb_soc tegra186_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = true, .ops = &tegra124_ops, .mbox = { .cmd = 0xe4, @@ -2673,6 +2680,7 @@ static const struct tegra_xusb_soc tegra194_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra124_ops, .mbox = { .cmd = 0x68, @@ -2708,6 +2716,7 @@ static const struct tegra_xusb_soc tegra234_soc = { .scale_ss_clock = false, .has_ipfs = false, .otg_reset_sspi = false, + .otg_set_port_power = false, .ops = &tegra234_ops, .mbox = { .cmd = XUSB_BAR2_ARU_MBOX_CMD, diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 48bb9bfb2204..333ab79f0ca9 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -337,7 +337,6 @@ static int omap2430_probe(struct platform_device *pdev) } else { device_set_of_node_from_dev(&musb->dev, &pdev->dev); } - of_node_put(np); glue->dev = &pdev->dev; glue->musb = musb; @@ -455,6 +454,7 @@ static int omap2430_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to register musb device\n"); goto err_disable_rpm; } + of_node_put(np); return 0; @@ -464,6 +464,7 @@ err_put_control_otghs: if (!IS_ERR(glue->control_otghs)) put_device(glue->control_otghs); err_put_musb: + of_node_put(np); platform_device_put(musb); return ret; diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 38ac910b1082..7bbd9523d4e9 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -194,6 +194,9 @@ static void belkin_sa_read_int_callback(struct urb *urb) usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data); + if (urb->actual_length < BELKIN_SA_MSR_INDEX + 1) + goto exit; + /* Handle known interrupt data */ /* ignore data[0] and data[1] */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index afff1a0f4298..bcf302e88ca4 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -445,6 +445,14 @@ static int cypress_generic_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * The buffer must be large enough for the one or two-byte header (and + * following data), but assume anything smaller than eight bytes is + * broken. + */ + if (port->interrupt_out_size < 8) + return -EINVAL; + priv = kzalloc_obj(struct cypress_private); if (!priv) return -ENOMEM; @@ -1017,8 +1025,8 @@ static void cypress_read_int_callback(struct urb *urb) char tty_flag = TTY_NORMAL; int bytes = 0; int result; - int i = 0; int status = urb->status; + int i; switch (status) { case 0: /* success */ @@ -1056,22 +1064,32 @@ static void cypress_read_int_callback(struct urb *urb) spin_lock_irqsave(&priv->lock, flags); result = urb->actual_length; + i = 0; switch (priv->pkt_fmt) { default: case packet_format_1: /* This is for the CY7C64013... */ + if (result < 2) + break; priv->current_status = data[0] & 0xF8; bytes = data[1] + 2; i = 2; break; case packet_format_2: /* This is for the CY7C63743... */ + if (result < 1) + break; priv->current_status = data[0] & 0xF8; bytes = (data[0] & 0x07) + 1; i = 1; break; } spin_unlock_irqrestore(&priv->lock, flags); + if (i == 0) { + dev_dbg(dev, "%s - short packet received: %d bytes\n", + __func__, result); + goto continue_read; + } if (result < bytes) { dev_dbg(dev, "%s - wrong packet size - received %d bytes but packet said %d bytes\n", diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index d515df045c4c..c481208255eb 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -1229,15 +1229,34 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num) static int digi_startup(struct usb_serial *serial) { struct digi_serial *serial_priv; + int oob_port_num; int ret; + int i; + + /* + * The port bulk-out buffers must be large enough for header and + * buffered data. + */ + for (i = 0; i < serial->type->num_ports; i++) { + if (serial->port[i]->bulk_out_size < DIGI_OUT_BUF_SIZE + 2) + return -EINVAL; + } + + /* + * The OOB port bulk-out buffer must be large enough for the two + * commands in digi_set_modem_signals(). + */ + oob_port_num = serial->type->num_ports; + if (serial->port[oob_port_num]->bulk_out_size < 8) + return -EINVAL; serial_priv = kzalloc_obj(*serial_priv); if (!serial_priv) return -ENOMEM; spin_lock_init(&serial_priv->ds_serial_lock); - serial_priv->ds_oob_port_num = serial->type->num_ports; - serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num]; + serial_priv->ds_oob_port_num = oob_port_num; + serial_priv->ds_oob_port = serial->port[oob_port_num]; ret = digi_port_init(serial_priv->ds_oob_port, serial_priv->ds_oob_port_num); diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 46448843541a..28b80607cebd 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -1187,6 +1187,10 @@ static void usa49wg_indat_callback(struct urb *urb) len = 0; while (i < urb->actual_length) { + if (urb->actual_length - i < 3) { + dev_warn_ratelimited(&urb->dev->dev, "malformed indat packet\n"); + break; + } /* Check port number from message */ if (data[i] >= serial->num_ports) { diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 18844b92bd08..163161881d2d 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -378,6 +378,7 @@ static int mct_u232_port_probe(struct usb_serial_port *port) { struct usb_serial *serial = port->serial; struct mct_u232_private *priv; + u16 pid; /* check first to simplify error handling */ if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) { @@ -385,6 +386,16 @@ static int mct_u232_port_probe(struct usb_serial_port *port) return -ENODEV; } + /* + * Compensate for a hardware bug: although the Sitecom U232-P25 + * device reports a maximum output packet size of 32 bytes, + * it seems to be able to accept only 16 bytes (and that's what + * SniffUSB says too...) + */ + pid = le16_to_cpu(serial->dev->descriptor.idProduct); + if (pid == MCT_U232_SITECOM_PID) + port->bulk_out_size = min(16, port->bulk_out_size); + priv = kzalloc_obj(*priv); if (!priv) return -ENOMEM; @@ -410,7 +421,6 @@ static void mct_u232_port_remove(struct usb_serial_port *port) static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) { - struct usb_serial *serial = port->serial; struct mct_u232_private *priv = usb_get_serial_port_data(port); int retval = 0; unsigned int control_state; @@ -418,15 +428,6 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port) unsigned char last_lcr; unsigned char last_msr; - /* Compensate for a hardware bug: although the Sitecom U232-P25 - * device reports a maximum output packet size of 32 bytes, - * it seems to be able to accept only 16 bytes (and that's what - * SniffUSB says too...) - */ - if (le16_to_cpu(serial->dev->descriptor.idProduct) - == MCT_U232_SITECOM_PID) - port->bulk_out_size = 16; - /* Do a defined restart: the normal serial device seems to * always turn on DTR and RTS here, so do the same. I'm not * sure if this is really necessary. But it should not harm @@ -543,6 +544,11 @@ static void mct_u232_read_int_callback(struct urb *urb) goto exit; } + if (urb->actual_length < 2) { + dev_warn_ratelimited(&port->dev, "short interrupt-in packet\n"); + goto exit; + } + /* * The interrupt-in pipe signals exceptional conditions (modem line * signal changes and errors). data[0] holds MSR, data[1] holds LSR. diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index ad5fdf55a02e..c9b9928c473a 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct usb_serial *serial, */ BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16); + /* + * The bulk-out buffers must be large enough for the four-byte header + * (and following data), but assume anything smaller than eight bytes + * is broken. + */ + if (usb_endpoint_maxp(epds->bulk_out[0]) < 8) + return -EINVAL; + for (i = 1; i < num_ports; ++i) epds->bulk_out[i] = epds->bulk_out[0]; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index aa1e9745f967..b59982ed8b25 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -30,6 +30,10 @@ /* This one seems to be a re-branded ZyXEL device */ #define BT_IGNITIONPRO_ID 0x2000 +#define OMNINET_HEADERLEN 4 +#define OMNINET_BULKOUTSIZE 64 +#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) + /* function prototypes */ static void omninet_process_read_urb(struct urb *urb); static int omninet_prepare_write_buffer(struct usb_serial_port *port, @@ -54,6 +58,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .description = "ZyXEL - omni.net usb", .id_table = id_table, .num_bulk_out = 2, + .bulk_out_size = OMNINET_BULKOUTSIZE, .calc_num_ports = omninet_calc_num_ports, .port_probe = omninet_port_probe, .port_remove = omninet_port_remove, @@ -130,10 +135,6 @@ static void omninet_port_remove(struct usb_serial_port *port) kfree(od); } -#define OMNINET_HEADERLEN 4 -#define OMNINET_BULKOUTSIZE 64 -#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN) - static void omninet_process_read_urb(struct urb *urb) { struct usb_serial_port *port = urb->context; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 42e4cecd28ac..48ae0188f2e9 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -2450,6 +2450,12 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */ { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x60) }, /* MeiG SRM813Q (NMEA) */ + { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */ { USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */ @@ -2470,7 +2476,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */ .driver_info = RSVD(5) }, - { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */ + { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */ + .driver_info = RSVD(5) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */ { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) }, { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) }, diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c index 238b54993446..d267a31dcccf 100644 --- a/drivers/usb/serial/safe_serial.c +++ b/drivers/usb/serial/safe_serial.c @@ -259,6 +259,7 @@ static int safe_prepare_write_buffer(struct usb_serial_port *port, static int safe_startup(struct usb_serial *serial) { struct usb_interface_descriptor *desc; + int bulk_out_size; if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS) return -ENODEV; @@ -279,6 +280,16 @@ static int safe_startup(struct usb_serial *serial) default: return -EINVAL; } + + /* + * The bulk-out buffer needs to be large enough for the two-byte + * trailer in safe mode, but assume anything smaller than eight bytes + * is broken. + */ + bulk_out_size = serial->port[0]->bulk_out_size; + if (bulk_out_size > 0 && bulk_out_size < 8) + return -EINVAL; + return 0; } diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h index 939a98c2d3f7..d6f86d5db3bf 100644 --- a/drivers/usb/storage/unusual_uas.h +++ b/drivers/usb/storage/unusual_uas.h @@ -132,6 +132,13 @@ UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999, USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NO_REPORT_OPCODES), +/* Reported-by: Sam Burkels <sam@1a38.nl> */ +UNUSUAL_DEV(0x154b, 0xf009, 0x0000, 0x9999, + "PNY", + "PNY ELITE PSSD", + USB_SC_DEVICE, USB_PR_DEVICE, NULL, + US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES), + /* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */ UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999, "PNY", diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index 35d9c3086990..263a89c5f324 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -405,6 +405,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt, dp->state = DP_STATE_EXIT_PRIME; break; case DP_CMD_STATUS_UPDATE: + if (count < 2) + break; dp->data.status = *vdo; ret = dp_altmode_status_update(dp); break; diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 889c4c29c1b8..9ab1277b7ed1 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1751,19 +1751,22 @@ static int fusb302_probe(struct i2c_client *client) bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode)); if (IS_ERR(bridge_dev)) { - ret = PTR_ERR(bridge_dev); - dev_err_probe(chip->dev, ret, "failed to alloc bridge\n"); - goto destroy_workqueue; + ret = dev_err_probe(chip->dev, PTR_ERR(bridge_dev), + "failed to alloc bridge\n"); + goto fwnode_put; } chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev); if (IS_ERR(chip->tcpm_port)) { - fwnode_handle_put(chip->tcpc_dev.fwnode); ret = dev_err_probe(dev, PTR_ERR(chip->tcpm_port), "cannot register tcpm port\n"); - goto destroy_workqueue; + goto fwnode_put; } + ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); + if (ret) + goto tcpm_unregister_port; + ret = request_threaded_irq(chip->gpio_int_n_irq, NULL, fusb302_irq_intn, IRQF_ONESHOT | IRQF_TRIGGER_LOW, "fsc_interrupt_int_n", chip); @@ -1774,14 +1777,11 @@ static int fusb302_probe(struct i2c_client *client) enable_irq_wake(chip->gpio_int_n_irq); i2c_set_clientdata(client, chip); - ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev); - if (ret) - return ret; - - return ret; + return 0; tcpm_unregister_port: tcpm_unregister_port(chip->tcpm_port); +fwnode_put: fwnode_handle_put(chip->tcpc_dev.fwnode); destroy_workqueue: fusb302_debugfs_exit(chip); diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index c0ee7e6959ed..7324139d51c8 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -181,6 +181,15 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status) rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET; msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr); rx_buf_ptr = rx_buf_ptr + sizeof(msg.header); + + if (count < TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET + sizeof(msg.header) + + pd_header_cnt_le(msg.header) * sizeof(msg.payload[0])) { + max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS); + dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d for header cnt %d\n", + count, pd_header_cnt_le(msg.header)); + return; + } + for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++, rx_buf_ptr += sizeof(msg.payload[0])) msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 55fee96d3342..7ef746a90a17 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1855,6 +1855,9 @@ static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt) u32 vdo = p[VDO_INDEX_IDH]; u32 product = p[VDO_INDEX_PRODUCT]; + if (cnt <= VDO_INDEX_PRODUCT) + return; + memset(&port->mode_data, 0, sizeof(port->mode_data)); port->partner_ident.id_header = vdo; @@ -1875,6 +1878,9 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p u32 product = p[VDO_INDEX_PRODUCT]; int svdm_version; + if (cnt <= VDO_INDEX_CABLE_1) + return; + /* * Attempt to consume identity only if cable currently is not set */ @@ -1898,7 +1904,7 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p switch (port->negotiated_rev_prime) { case PD_REV30: port->cable_desc.pd_revision = 0x0300; - if (port->cable_desc.active) + if (port->cable_desc.active && cnt > VDO_INDEX_CABLE_2) port->cable_ident.vdo[1] = p[VDO_INDEX_CABLE_2]; break; case PD_REV20: @@ -1986,23 +1992,19 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt, switch (rx_sop_type) { case TCPC_TX_SOP_PRIME: pmdata = &port->mode_data_prime; - if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; case TCPC_TX_SOP: pmdata = &port->mode_data; - if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) { - /* Already logged in svdm_consume_svids() */ - return; - } break; default: return; } for (i = 1; i < cnt; i++) { + if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) { + /* Already logged in svdm_consume_svids() */ + return; + } paltmode = &pmdata->altmode_desc[pmdata->altmodes]; memset(paltmode, 0, sizeof(*paltmode)); @@ -2147,6 +2149,55 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port) tcpm_can_communicate_sop_prime(port); } +static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) +{ + struct typec_port *typec = port->typec_port; + struct pd_mode_data *modep; + + if (rx_sop_type == TCPC_TX_SOP) { + modep = &port->mode_data; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP; + response[0] = VDO(svid, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + if (tcpm_cable_vdm_supported(port)) { + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(USB_SID_PD, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_SVID); + return 1; + } + + tcpm_register_partner_altmodes(port); + } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { + modep = &port->mode_data_prime; + modep->svid_index++; + + if (modep->svid_index < modep->nsvids) { + u16 svid = modep->svids[modep->svid_index]; + *response_tx_sop_type = TCPC_TX_SOP_PRIME; + response[0] = VDO(svid, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_MODES); + return 1; + } + + tcpm_register_plug_altmodes(port); + tcpm_register_partner_altmodes(port); + } + + return 0; +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, enum adev_actions *adev_action, @@ -2404,41 +2455,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, } break; case CMD_DISCOVER_MODES: - if (rx_sop_type == TCPC_TX_SOP) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep->svid_index++; - if (modep->svid_index < modep->nsvids) { - u16 svid = modep->svids[modep->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP; - response[0] = VDO(svid, 1, svdm_version, - CMD_DISCOVER_MODES); - rlen = 1; - } else if (tcpm_cable_vdm_supported(port)) { - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(USB_SID_PD, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_SVID); - rlen = 1; - } else { - tcpm_register_partner_altmodes(port); - } - } else if (rx_sop_type == TCPC_TX_SOP_PRIME) { - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt, rx_sop_type); - modep_prime->svid_index++; - if (modep_prime->svid_index < modep_prime->nsvids) { - u16 svid = modep_prime->svids[modep_prime->svid_index]; - *response_tx_sop_type = TCPC_TX_SOP_PRIME; - response[0] = VDO(svid, 1, - typec_get_cable_svdm_version(typec), - CMD_DISCOVER_MODES); - rlen = 1; - } else { - tcpm_register_plug_altmodes(port); - tcpm_register_partner_altmodes(port); - } - } + /* 6.4.4.3.3 */ + svdm_consume_modes(port, p, cnt, rx_sop_type); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); break; case CMD_ENTER_MODE: *response_tx_sop_type = rx_sop_type; @@ -2481,9 +2502,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, switch (cmd) { case CMD_DISCOVER_IDENT: case CMD_DISCOVER_SVID: - case CMD_DISCOVER_MODES: case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): break; + case CMD_DISCOVER_MODES: + tcpm_log(port, "Skip SVID 0x%04x (failed to discover mode)", + PD_VDO_SVID_SVID0(p[0])); + rlen = tcpm_handle_discover_mode(port, response, + rx_sop_type, + response_tx_sop_type); + break; case CMD_ENTER_MODE: /* Back to USB Operation */ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM; diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 759c982bb16a..0e5a3e277c3e 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -444,9 +444,11 @@ static int wcove_start_toggling(struct tcpc_dev *tcpc, return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl); } -static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) +static int wcove_read_rx_buffer(struct wcove_typec *wcove, + struct pd_message *msg) { - unsigned int info; + unsigned int info, val, len; + u8 *buf = (u8 *)msg; int ret; int i; @@ -454,12 +456,13 @@ static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg) if (ret) return ret; - /* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */ + len = min(USBC_RXINFO_RXBYTES(info), sizeof(*msg)); - for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) { - ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i); + for (i = 0; i < len; i++) { + ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, &val); if (ret) return ret; + buf[i] = val; } return regmap_write(wcove->regmap, USBC_RXSTATUS, diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 43faec794b95..d0b769333bd9 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1835,6 +1835,7 @@ static int tps6598x_probe(struct i2c_client *client) goto err_role_put; if (status & TPS_STATUS_PLUG_PRESENT) { + ret = -EINVAL; if (!tps6598x_read_power_status(tps)) goto err_unregister_port; if (!tps->data->read_data_status(tps)) diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c index 8aae80b457d7..67a0991a7b76 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -240,6 +240,10 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt, dp->header |= VDO_CMDT(CMDT_RSP_ACK); break; case DP_CMD_CONFIGURE: + if (count < 2) { + dp->header |= VDO_CMDT(CMDT_RSP_NAK); + break; + } dp->data.conf = *data; if (ucsi_displayport_configure(dp)) { dp->header |= VDO_CMDT(CMDT_RSP_NAK); diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 5b7ad9e99cb9..61cb24ed820f 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -1277,7 +1277,7 @@ static void ucsi_handle_connector_change(struct work_struct *work) work); struct ucsi *ucsi = con->ucsi; u8 curr_scale, volt_scale; - enum typec_role role; + enum typec_role role, prev_role; u16 change; int ret; u32 val; @@ -1288,6 +1288,8 @@ static void ucsi_handle_connector_change(struct work_struct *work) dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n", __func__); + prev_role = UCSI_CONSTAT(con, PWR_DIR); + ret = ucsi_get_connector_status(con, true); if (ret) { dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n", @@ -1304,9 +1306,14 @@ static void ucsi_handle_connector_change(struct work_struct *work) change = UCSI_CONSTAT(con, CHANGE); role = UCSI_CONSTAT(con, PWR_DIR); - if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) { + if ((change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) { typec_set_pwr_role(con->port, role); - ucsi_port_psy_changed(con); + + /* Some power_supply properties vary depending on the power direction when + * connected + */ + if (UCSI_CONSTAT(con, CONNECTED)) + ucsi_port_psy_changed(con); /* Complete pending power role swap */ if (!completion_done(&con->complete)) @@ -1380,13 +1387,22 @@ out_unlock: */ void ucsi_connector_change(struct ucsi *ucsi, u8 num) { - struct ucsi_connector *con = &ucsi->connector[num - 1]; + struct ucsi_connector *con; if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) { dev_dbg(ucsi->dev, "Early connector change event\n"); return; } + if (!num || num > ucsi->cap.num_connectors) { + dev_warn_ratelimited(ucsi->dev, + "Bogus connector change on %u (max %u)\n", + num, ucsi->cap.num_connectors); + return; + } + + con = &ucsi->connector[num - 1]; + if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags)) schedule_work(&con->work); } diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 199799b319c2..4463c1ae96bd 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1243,6 +1243,11 @@ not_signed_fw: *****************************************************************/ p = strnchr(fw->data, fw->size, ':'); + if (!p) { + dev_err(dev, "Bad FW format: no ':' record header found\n"); + err = -EINVAL; + goto release_mem; + } while (p < eof) { s = strnchr(p + 1, eof - p - 1, ':'); diff --git a/drivers/usb/usbip/vudc_dev.c b/drivers/usb/usbip/vudc_dev.c index 90383107b660..c5f079c5a1ea 100644 --- a/drivers/usb/usbip/vudc_dev.c +++ b/drivers/usb/usbip/vudc_dev.c @@ -632,6 +632,7 @@ void vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); + v_stop_timer(udc); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); diff --git a/drivers/usb/usbip/vudc_transfer.c b/drivers/usb/usbip/vudc_transfer.c index a4f02ea3e3ef..d4ce85c4c6a2 100644 --- a/drivers/usb/usbip/vudc_transfer.c +++ b/drivers/usb/usbip/vudc_transfer.c @@ -490,7 +490,8 @@ void v_stop_timer(struct vudc *udc) { struct transfer_timer *t = &udc->tr_timer; - /* timer itself will take care of stopping */ + /* Delete the timer synchronously before teardown frees udc. */ dev_dbg(&udc->pdev->dev, "timer stop"); + timer_delete_sync(&t->timer); t->state = VUDC_TR_STOPPED; } |
