]>
Commit | Line | Data |
---|---|---|
d86bd7b0 GKH |
1 | From fc834e607ae3d18e1a20bca3f9a2d7f52ea7a2be Mon Sep 17 00:00:00 2001 |
2 | From: Alan Stern <stern@rowland.harvard.edu> | |
3 | Date: Thu, 18 Apr 2019 13:12:07 -0400 | |
4 | Subject: USB: dummy-hcd: Fix failure to give back unlinked URBs | |
5 | ||
6 | From: Alan Stern <stern@rowland.harvard.edu> | |
7 | ||
8 | commit fc834e607ae3d18e1a20bca3f9a2d7f52ea7a2be upstream. | |
9 | ||
10 | The syzkaller USB fuzzer identified a failure mode in which dummy-hcd | |
11 | would never give back an unlinked URB. This causes usb_kill_urb() to | |
12 | hang, leading to WARNINGs and unkillable threads. | |
13 | ||
14 | In dummy-hcd, all URBs are given back by the dummy_timer() routine as | |
15 | it scans through the list of pending URBS. Failure to give back URBs | |
16 | can be caused by failure to start or early exit from the scanning | |
17 | loop. The code currently has two such pathways: One is triggered when | |
18 | an unsupported bus transfer speed is encountered, and the other by | |
19 | exhausting the simulated bandwidth for USB transfers during a frame. | |
20 | ||
21 | This patch removes those two paths, thereby allowing all unlinked URBs | |
22 | to be given back in a timely manner. It adds a check for the bus | |
23 | speed when the gadget first starts running, so that dummy_timer() will | |
24 | never thereafter encounter an unsupported speed. And it prevents the | |
25 | loop from exiting as soon as the total bandwidth has been used up (the | |
26 | scanning loop continues, giving back unlinked URBs as they are found, | |
27 | but not transferring any more data). | |
28 | ||
29 | Thanks to Andrey Konovalov for manually running the syzkaller fuzzer | |
30 | to help track down the source of the bug. | |
31 | ||
32 | Signed-off-by: Alan Stern <stern@rowland.harvard.edu> | |
33 | Reported-and-tested-by: syzbot+d919b0f29d7b5a4994b9@syzkaller.appspotmail.com | |
34 | CC: <stable@vger.kernel.org> | |
35 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
36 | ||
37 | --- | |
38 | drivers/usb/gadget/udc/dummy_hcd.c | 19 +++++++++++++++---- | |
39 | 1 file changed, 15 insertions(+), 4 deletions(-) | |
40 | ||
41 | --- a/drivers/usb/gadget/udc/dummy_hcd.c | |
42 | +++ b/drivers/usb/gadget/udc/dummy_hcd.c | |
43 | @@ -979,8 +979,18 @@ static int dummy_udc_start(struct usb_ga | |
44 | struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g); | |
45 | struct dummy *dum = dum_hcd->dum; | |
46 | ||
47 | - if (driver->max_speed == USB_SPEED_UNKNOWN) | |
48 | + switch (g->speed) { | |
49 | + /* All the speeds we support */ | |
50 | + case USB_SPEED_LOW: | |
51 | + case USB_SPEED_FULL: | |
52 | + case USB_SPEED_HIGH: | |
53 | + case USB_SPEED_SUPER: | |
54 | + break; | |
55 | + default: | |
56 | + dev_err(dummy_dev(dum_hcd), "Unsupported driver max speed %d\n", | |
57 | + driver->max_speed); | |
58 | return -EINVAL; | |
59 | + } | |
60 | ||
61 | /* | |
62 | * SLAVE side init ... the layer above hardware, which | |
63 | @@ -1784,9 +1794,10 @@ static void dummy_timer(struct timer_lis | |
64 | /* Bus speed is 500000 bytes/ms, so use a little less */ | |
65 | total = 490000; | |
66 | break; | |
67 | - default: | |
68 | + default: /* Can't happen */ | |
69 | dev_err(dummy_dev(dum_hcd), "bogus device speed\n"); | |
70 | - return; | |
71 | + total = 0; | |
72 | + break; | |
73 | } | |
74 | ||
75 | /* FIXME if HZ != 1000 this will probably misbehave ... */ | |
76 | @@ -1828,7 +1839,7 @@ restart: | |
77 | ||
78 | /* Used up this frame's bandwidth? */ | |
79 | if (total <= 0) | |
80 | - break; | |
81 | + continue; | |
82 | ||
83 | /* find the gadget's ep for this request (if configured) */ | |
84 | address = usb_pipeendpoint (urb->pipe); |