]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.4.57/usb-don-t-free-bandwidth_mutex-too-early.patch
5.0-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 4.4.57 / usb-don-t-free-bandwidth_mutex-too-early.patch
1 From ab2a4bf83902c170d29ba130a8abb5f9d90559e1 Mon Sep 17 00:00:00 2001
2 From: Alan Stern <stern@rowland.harvard.edu>
3 Date: Mon, 27 Jun 2016 10:23:10 -0400
4 Subject: USB: don't free bandwidth_mutex too early
5
6 From: Alan Stern <stern@rowland.harvard.edu>
7
8 commit ab2a4bf83902c170d29ba130a8abb5f9d90559e1 upstream.
9
10 The USB core contains a bug that can show up when a USB-3 host
11 controller is removed. If the primary (USB-2) hcd structure is
12 released before the shared (USB-3) hcd, the core will try to do a
13 double-free of the common bandwidth_mutex.
14
15 The problem was described in graphical form by Chung-Geol Kim, who
16 first reported it:
17
18 =================================================
19 At *remove USB(3.0) Storage
20 sequence <1> --> <5> ((Problem Case))
21 =================================================
22 VOLD
23 ------------------------------------|------------
24 (uevent)
25 ________|_________
26 |<1> |
27 |dwc3_otg_sm_work |
28 |usb_put_hcd |
29 |peer_hcd(kref=2)|
30 |__________________|
31 ________|_________
32 |<2> |
33 |New USB BUS #2 |
34 | |
35 |peer_hcd(kref=1) |
36 | |
37 --(Link)-bandXX_mutex|
38 | |__________________|
39 |
40 ___________________ |
41 |<3> | |
42 |dwc3_otg_sm_work | |
43 |usb_put_hcd | |
44 |primary_hcd(kref=1)| |
45 |___________________| |
46 _________|_________ |
47 |<4> | |
48 |New USB BUS #1 | |
49 |hcd_release | |
50 |primary_hcd(kref=0)| |
51 | | |
52 |bandXX_mutex(free) |<-
53 |___________________|
54 (( VOLD ))
55 ______|___________
56 |<5> |
57 | SCSI |
58 |usb_put_hcd |
59 |peer_hcd(kref=0) |
60 |*hcd_release |
61 |bandXX_mutex(free*)|<- double free
62 |__________________|
63
64 =================================================
65
66 This happens because hcd_release() frees the bandwidth_mutex whenever
67 it sees a primary hcd being released (which is not a very good idea
68 in any case), but in the course of releasing the primary hcd, it
69 changes the pointers in the shared hcd in such a way that the shared
70 hcd will appear to be primary when it gets released.
71
72 This patch fixes the problem by changing hcd_release() so that it
73 deallocates the bandwidth_mutex only when the _last_ hcd structure
74 referencing it is released. The patch also removes an unnecessary
75 test, so that when an hcd is released, both the shared_hcd and
76 primary_hcd pointers in the hcd's peer will be cleared.
77
78 Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
79 Reported-by: Chung-Geol Kim <chunggeol.kim@samsung.com>
80 Tested-by: Chung-Geol Kim <chunggeol.kim@samsung.com>
81 Cc: Sumit Semwal <sumit.semwal@linaro.org>
82 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
83
84
85 ---
86 drivers/usb/core/hcd.c | 17 +++++++----------
87 1 file changed, 7 insertions(+), 10 deletions(-)
88
89 --- a/drivers/usb/core/hcd.c
90 +++ b/drivers/usb/core/hcd.c
91 @@ -2573,26 +2573,23 @@ EXPORT_SYMBOL_GPL(usb_create_hcd);
92 * Don't deallocate the bandwidth_mutex until the last shared usb_hcd is
93 * deallocated.
94 *
95 - * Make sure to only deallocate the bandwidth_mutex when the primary HCD is
96 - * freed. When hcd_release() is called for either hcd in a peer set
97 - * invalidate the peer's ->shared_hcd and ->primary_hcd pointers to
98 - * block new peering attempts
99 + * Make sure to deallocate the bandwidth_mutex only when the last HCD is
100 + * freed. When hcd_release() is called for either hcd in a peer set,
101 + * invalidate the peer's ->shared_hcd and ->primary_hcd pointers.
102 */
103 static void hcd_release(struct kref *kref)
104 {
105 struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
106
107 mutex_lock(&usb_port_peer_mutex);
108 - if (usb_hcd_is_primary_hcd(hcd)) {
109 - kfree(hcd->address0_mutex);
110 - kfree(hcd->bandwidth_mutex);
111 - }
112 if (hcd->shared_hcd) {
113 struct usb_hcd *peer = hcd->shared_hcd;
114
115 peer->shared_hcd = NULL;
116 - if (peer->primary_hcd == hcd)
117 - peer->primary_hcd = NULL;
118 + peer->primary_hcd = NULL;
119 + } else {
120 + kfree(hcd->address0_mutex);
121 + kfree(hcd->bandwidth_mutex);
122 }
123 mutex_unlock(&usb_port_peer_mutex);
124 kfree(hcd);