aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2017-09-26 15:15:40 -0400
committerFelipe Balbi <felipe.balbi@linux.intel.com>2017-09-28 05:30:56 -0400
commit0173a68bfb0ad1c72a6ee39cc485aa2c97540b98 (patch)
treecc4ab357e990d4701b42df754b37f1a006063f9b
parentfe659bcc9b173bcfdd958ce2aec75e47651e74e1 (diff)
USB: dummy-hcd: fix infinite-loop resubmission bug
The dummy-hcd HCD/UDC emulator tries not to do too much work during each timer interrupt. But it doesn't try very hard; currently all it does is limit the total amount of bulk data transferred. Other transfer types aren't limited, and URBs that transfer no data (because of an error, perhaps) don't count toward the limit, even though on a real USB bus they would consume at least a minimum overhead. This means it's possible to get the driver stuck in an infinite loop, for example, if the host class driver resubmits an URB every time it completes (which is common for interrupt URBs). Each time the URB is resubmitted it gets added to the end of the pending-URBs list, and dummy-hcd doesn't stop until that list is empty. Andrey Konovalov was able to trigger this failure mode using the syzkaller fuzzer. This patch fixes the infinite-loop problem by restricting the URBs handled during each timer interrupt to those that were already on the pending list when the interrupt routine started. Newly added URBs won't be processed until the next timer interrupt. The problem of properly accounting for non-bulk bandwidth (as well as packet and transaction overhead) is not addressed here. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: Andrey Konovalov <andreyknvl@google.com> Tested-by: Andrey Konovalov <andreyknvl@google.com> CC: <stable@vger.kernel.org> Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c9
1 files changed, 9 insertions, 0 deletions
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index d515ec31afe4..b2ab9cc33fec 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -237,6 +237,8 @@ struct dummy_hcd {
237 237
238 struct usb_device *udev; 238 struct usb_device *udev;
239 struct list_head urbp_list; 239 struct list_head urbp_list;
240 struct urbp *next_frame_urbp;
241
240 u32 stream_en_ep; 242 u32 stream_en_ep;
241 u8 num_stream[30 / 2]; 243 u8 num_stream[30 / 2];
242 244
@@ -1250,6 +1252,8 @@ static int dummy_urb_enqueue(
1250 1252
1251 list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list); 1253 list_add_tail(&urbp->urbp_list, &dum_hcd->urbp_list);
1252 urb->hcpriv = urbp; 1254 urb->hcpriv = urbp;
1255 if (!dum_hcd->next_frame_urbp)
1256 dum_hcd->next_frame_urbp = urbp;
1253 if (usb_pipetype(urb->pipe) == PIPE_CONTROL) 1257 if (usb_pipetype(urb->pipe) == PIPE_CONTROL)
1254 urb->error_count = 1; /* mark as a new urb */ 1258 urb->error_count = 1; /* mark as a new urb */
1255 1259
@@ -1766,6 +1770,7 @@ static void dummy_timer(unsigned long _dum_hcd)
1766 spin_unlock_irqrestore(&dum->lock, flags); 1770 spin_unlock_irqrestore(&dum->lock, flags);
1767 return; 1771 return;
1768 } 1772 }
1773 dum_hcd->next_frame_urbp = NULL;
1769 1774
1770 for (i = 0; i < DUMMY_ENDPOINTS; i++) { 1775 for (i = 0; i < DUMMY_ENDPOINTS; i++) {
1771 if (!ep_info[i].name) 1776 if (!ep_info[i].name)
@@ -1782,6 +1787,10 @@ restart:
1782 int type; 1787 int type;
1783 int status = -EINPROGRESS; 1788 int status = -EINPROGRESS;
1784 1789
1790 /* stop when we reach URBs queued after the timer interrupt */
1791 if (urbp == dum_hcd->next_frame_urbp)
1792 break;
1793
1785 urb = urbp->urb; 1794 urb = urbp->urb;
1786 if (urb->unlinked) 1795 if (urb->unlinked)
1787 goto return_urb; 1796 goto return_urb;