aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorShuah Khan (Samsung OSG) <shuah@kernel.org>2018-05-14 22:49:58 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-05-15 03:52:02 -0400
commit22076557b07c12086eeb16b8ce2b0b735f7a27e7 (patch)
treec568062087d8f520520d13239f603fbdf2b45ce3 /drivers
parent7510df3f29d44685bab7b1918b61a8ccd57126a9 (diff)
usbip: usbip_host: fix NULL-ptr deref and use-after-free errors
usbip_host updates device status without holding lock from stub probe, disconnect and rebind code paths. When multiple requests to import a device are received, these unprotected code paths step all over each other and drive fails with NULL-ptr deref and use-after-free errors. The driver uses a table lock to protect the busid array for adding and deleting busids to the table. However, the probe, disconnect and rebind paths get the busid table entry and update the status without holding the busid table lock. Add a new finer grain lock to protect the busid entry. This new lock will be held to search and update the busid entry fields from get_busid_idx(), add_match_busid() and del_match_busid(). match_busid_show() does the same to access the busid entry fields. get_busid_priv() changed to return the pointer to the busid entry holding the busid lock. stub_probe(), stub_disconnect() and stub_device_rebind() call put_busid_priv() to release the busid lock before returning. This changes fixes the unprotected code paths eliminating the race conditions in updating the busid entries. Reported-by: Jakub Jirasek Signed-off-by: Shuah Khan (Samsung OSG) <shuah@kernel.org> Cc: stable <stable@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/usbip/stub.h2
-rw-r--r--drivers/usb/usbip/stub_dev.c33
-rw-r--r--drivers/usb/usbip/stub_main.c40
3 files changed, 60 insertions, 15 deletions
diff --git a/drivers/usb/usbip/stub.h b/drivers/usb/usbip/stub.h
index 14a72357800a..35618ceb2791 100644
--- a/drivers/usb/usbip/stub.h
+++ b/drivers/usb/usbip/stub.h
@@ -73,6 +73,7 @@ struct bus_id_priv {
73 struct stub_device *sdev; 73 struct stub_device *sdev;
74 struct usb_device *udev; 74 struct usb_device *udev;
75 char shutdown_busid; 75 char shutdown_busid;
76 spinlock_t busid_lock;
76}; 77};
77 78
78/* stub_priv is allocated from stub_priv_cache */ 79/* stub_priv is allocated from stub_priv_cache */
@@ -83,6 +84,7 @@ extern struct usb_device_driver stub_driver;
83 84
84/* stub_main.c */ 85/* stub_main.c */
85struct bus_id_priv *get_busid_priv(const char *busid); 86struct bus_id_priv *get_busid_priv(const char *busid);
87void put_busid_priv(struct bus_id_priv *bid);
86int del_match_busid(char *busid); 88int del_match_busid(char *busid);
87void stub_device_cleanup_urbs(struct stub_device *sdev); 89void stub_device_cleanup_urbs(struct stub_device *sdev);
88 90
diff --git a/drivers/usb/usbip/stub_dev.c b/drivers/usb/usbip/stub_dev.c
index 9d0425113c4b..c0d6ff1baa72 100644
--- a/drivers/usb/usbip/stub_dev.c
+++ b/drivers/usb/usbip/stub_dev.c
@@ -300,7 +300,7 @@ static int stub_probe(struct usb_device *udev)
300 struct stub_device *sdev = NULL; 300 struct stub_device *sdev = NULL;
301 const char *udev_busid = dev_name(&udev->dev); 301 const char *udev_busid = dev_name(&udev->dev);
302 struct bus_id_priv *busid_priv; 302 struct bus_id_priv *busid_priv;
303 int rc; 303 int rc = 0;
304 304
305 dev_dbg(&udev->dev, "Enter probe\n"); 305 dev_dbg(&udev->dev, "Enter probe\n");
306 306
@@ -317,13 +317,15 @@ static int stub_probe(struct usb_device *udev)
317 * other matched drivers by the driver core. 317 * other matched drivers by the driver core.
318 * See driver_probe_device() in driver/base/dd.c 318 * See driver_probe_device() in driver/base/dd.c
319 */ 319 */
320 return -ENODEV; 320 rc = -ENODEV;
321 goto call_put_busid_priv;
321 } 322 }
322 323
323 if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) { 324 if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
324 dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n", 325 dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
325 udev_busid); 326 udev_busid);
326 return -ENODEV; 327 rc = -ENODEV;
328 goto call_put_busid_priv;
327 } 329 }
328 330
329 if (!strcmp(udev->bus->bus_name, "vhci_hcd")) { 331 if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
@@ -331,13 +333,16 @@ static int stub_probe(struct usb_device *udev)
331 "%s is attached on vhci_hcd... skip!\n", 333 "%s is attached on vhci_hcd... skip!\n",
332 udev_busid); 334 udev_busid);
333 335
334 return -ENODEV; 336 rc = -ENODEV;
337 goto call_put_busid_priv;
335 } 338 }
336 339
337 /* ok, this is my device */ 340 /* ok, this is my device */
338 sdev = stub_device_alloc(udev); 341 sdev = stub_device_alloc(udev);
339 if (!sdev) 342 if (!sdev) {
340 return -ENOMEM; 343 rc = -ENOMEM;
344 goto call_put_busid_priv;
345 }
341 346
342 dev_info(&udev->dev, 347 dev_info(&udev->dev,
343 "usbip-host: register new device (bus %u dev %u)\n", 348 "usbip-host: register new device (bus %u dev %u)\n",
@@ -369,7 +374,9 @@ static int stub_probe(struct usb_device *udev)
369 } 374 }
370 busid_priv->status = STUB_BUSID_ALLOC; 375 busid_priv->status = STUB_BUSID_ALLOC;
371 376
372 return 0; 377 rc = 0;
378 goto call_put_busid_priv;
379
373err_files: 380err_files:
374 usb_hub_release_port(udev->parent, udev->portnum, 381 usb_hub_release_port(udev->parent, udev->portnum,
375 (struct usb_dev_state *) udev); 382 (struct usb_dev_state *) udev);
@@ -379,6 +386,9 @@ err_port:
379 386
380 busid_priv->sdev = NULL; 387 busid_priv->sdev = NULL;
381 stub_device_free(sdev); 388 stub_device_free(sdev);
389
390call_put_busid_priv:
391 put_busid_priv(busid_priv);
382 return rc; 392 return rc;
383} 393}
384 394
@@ -417,7 +427,7 @@ static void stub_disconnect(struct usb_device *udev)
417 /* get stub_device */ 427 /* get stub_device */
418 if (!sdev) { 428 if (!sdev) {
419 dev_err(&udev->dev, "could not get device"); 429 dev_err(&udev->dev, "could not get device");
420 return; 430 goto call_put_busid_priv;
421 } 431 }
422 432
423 dev_set_drvdata(&udev->dev, NULL); 433 dev_set_drvdata(&udev->dev, NULL);
@@ -432,12 +442,12 @@ static void stub_disconnect(struct usb_device *udev)
432 (struct usb_dev_state *) udev); 442 (struct usb_dev_state *) udev);
433 if (rc) { 443 if (rc) {
434 dev_dbg(&udev->dev, "unable to release port\n"); 444 dev_dbg(&udev->dev, "unable to release port\n");
435 return; 445 goto call_put_busid_priv;
436 } 446 }
437 447
438 /* If usb reset is called from event handler */ 448 /* If usb reset is called from event handler */
439 if (usbip_in_eh(current)) 449 if (usbip_in_eh(current))
440 return; 450 goto call_put_busid_priv;
441 451
442 /* shutdown the current connection */ 452 /* shutdown the current connection */
443 shutdown_busid(busid_priv); 453 shutdown_busid(busid_priv);
@@ -450,6 +460,9 @@ static void stub_disconnect(struct usb_device *udev)
450 460
451 if (busid_priv->status == STUB_BUSID_ALLOC) 461 if (busid_priv->status == STUB_BUSID_ALLOC)
452 busid_priv->status = STUB_BUSID_ADDED; 462 busid_priv->status = STUB_BUSID_ADDED;
463
464call_put_busid_priv:
465 put_busid_priv(busid_priv);
453} 466}
454 467
455#ifdef CONFIG_PM 468#ifdef CONFIG_PM
diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c
index 587b9bc10042..41c7b9de2a92 100644
--- a/drivers/usb/usbip/stub_main.c
+++ b/drivers/usb/usbip/stub_main.c
@@ -26,6 +26,8 @@ static spinlock_t busid_table_lock;
26 26
27static void init_busid_table(void) 27static void init_busid_table(void)
28{ 28{
29 int i;
30
29 /* 31 /*
30 * This also sets the bus_table[i].status to 32 * This also sets the bus_table[i].status to
31 * STUB_BUSID_OTHER, which is 0. 33 * STUB_BUSID_OTHER, which is 0.
@@ -33,6 +35,9 @@ static void init_busid_table(void)
33 memset(busid_table, 0, sizeof(busid_table)); 35 memset(busid_table, 0, sizeof(busid_table));
34 36
35 spin_lock_init(&busid_table_lock); 37 spin_lock_init(&busid_table_lock);
38
39 for (i = 0; i < MAX_BUSID; i++)
40 spin_lock_init(&busid_table[i].busid_lock);
36} 41}
37 42
38/* 43/*
@@ -44,15 +49,20 @@ static int get_busid_idx(const char *busid)
44 int i; 49 int i;
45 int idx = -1; 50 int idx = -1;
46 51
47 for (i = 0; i < MAX_BUSID; i++) 52 for (i = 0; i < MAX_BUSID; i++) {
53 spin_lock(&busid_table[i].busid_lock);
48 if (busid_table[i].name[0]) 54 if (busid_table[i].name[0])
49 if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) { 55 if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
50 idx = i; 56 idx = i;
57 spin_unlock(&busid_table[i].busid_lock);
51 break; 58 break;
52 } 59 }
60 spin_unlock(&busid_table[i].busid_lock);
61 }
53 return idx; 62 return idx;
54} 63}
55 64
65/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
56struct bus_id_priv *get_busid_priv(const char *busid) 66struct bus_id_priv *get_busid_priv(const char *busid)
57{ 67{
58 int idx; 68 int idx;
@@ -60,13 +70,21 @@ struct bus_id_priv *get_busid_priv(const char *busid)
60 70
61 spin_lock(&busid_table_lock); 71 spin_lock(&busid_table_lock);
62 idx = get_busid_idx(busid); 72 idx = get_busid_idx(busid);
63 if (idx >= 0) 73 if (idx >= 0) {
64 bid = &(busid_table[idx]); 74 bid = &(busid_table[idx]);
75 /* get busid_lock before returning */
76 spin_lock(&bid->busid_lock);
77 }
65 spin_unlock(&busid_table_lock); 78 spin_unlock(&busid_table_lock);
66 79
67 return bid; 80 return bid;
68} 81}
69 82
83void put_busid_priv(struct bus_id_priv *bid)
84{
85 spin_unlock(&bid->busid_lock);
86}
87
70static int add_match_busid(char *busid) 88static int add_match_busid(char *busid)
71{ 89{
72 int i; 90 int i;
@@ -79,15 +97,19 @@ static int add_match_busid(char *busid)
79 goto out; 97 goto out;
80 } 98 }
81 99
82 for (i = 0; i < MAX_BUSID; i++) 100 for (i = 0; i < MAX_BUSID; i++) {
101 spin_lock(&busid_table[i].busid_lock);
83 if (!busid_table[i].name[0]) { 102 if (!busid_table[i].name[0]) {
84 strlcpy(busid_table[i].name, busid, BUSID_SIZE); 103 strlcpy(busid_table[i].name, busid, BUSID_SIZE);
85 if ((busid_table[i].status != STUB_BUSID_ALLOC) && 104 if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
86 (busid_table[i].status != STUB_BUSID_REMOV)) 105 (busid_table[i].status != STUB_BUSID_REMOV))
87 busid_table[i].status = STUB_BUSID_ADDED; 106 busid_table[i].status = STUB_BUSID_ADDED;
88 ret = 0; 107 ret = 0;
108 spin_unlock(&busid_table[i].busid_lock);
89 break; 109 break;
90 } 110 }
111 spin_unlock(&busid_table[i].busid_lock);
112 }
91 113
92out: 114out:
93 spin_unlock(&busid_table_lock); 115 spin_unlock(&busid_table_lock);
@@ -108,6 +130,8 @@ int del_match_busid(char *busid)
108 /* found */ 130 /* found */
109 ret = 0; 131 ret = 0;
110 132
133 spin_lock(&busid_table[idx].busid_lock);
134
111 if (busid_table[idx].status == STUB_BUSID_OTHER) 135 if (busid_table[idx].status == STUB_BUSID_OTHER)
112 memset(busid_table[idx].name, 0, BUSID_SIZE); 136 memset(busid_table[idx].name, 0, BUSID_SIZE);
113 137
@@ -115,6 +139,7 @@ int del_match_busid(char *busid)
115 (busid_table[idx].status != STUB_BUSID_ADDED)) 139 (busid_table[idx].status != STUB_BUSID_ADDED))
116 busid_table[idx].status = STUB_BUSID_REMOV; 140 busid_table[idx].status = STUB_BUSID_REMOV;
117 141
142 spin_unlock(&busid_table[idx].busid_lock);
118out: 143out:
119 spin_unlock(&busid_table_lock); 144 spin_unlock(&busid_table_lock);
120 145
@@ -127,9 +152,12 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf)
127 char *out = buf; 152 char *out = buf;
128 153
129 spin_lock(&busid_table_lock); 154 spin_lock(&busid_table_lock);
130 for (i = 0; i < MAX_BUSID; i++) 155 for (i = 0; i < MAX_BUSID; i++) {
156 spin_lock(&busid_table[i].busid_lock);
131 if (busid_table[i].name[0]) 157 if (busid_table[i].name[0])
132 out += sprintf(out, "%s ", busid_table[i].name); 158 out += sprintf(out, "%s ", busid_table[i].name);
159 spin_unlock(&busid_table[i].busid_lock);
160 }
133 spin_unlock(&busid_table_lock); 161 spin_unlock(&busid_table_lock);
134 out += sprintf(out, "\n"); 162 out += sprintf(out, "\n");
135 163
@@ -204,7 +232,7 @@ static void stub_device_rebind(void)
204 } 232 }
205 spin_unlock(&busid_table_lock); 233 spin_unlock(&busid_table_lock);
206 234
207 /* now run rebind */ 235 /* now run rebind - no need to hold locks. driver files are removed */
208 for (i = 0; i < MAX_BUSID; i++) { 236 for (i = 0; i < MAX_BUSID; i++) {
209 if (busid_table[i].name[0] && 237 if (busid_table[i].name[0] &&
210 busid_table[i].shutdown_busid) { 238 busid_table[i].shutdown_busid) {
@@ -234,6 +262,8 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
234 262
235 /* mark the device for deletion so probe ignores it during rescan */ 263 /* mark the device for deletion so probe ignores it during rescan */
236 bid->status = STUB_BUSID_OTHER; 264 bid->status = STUB_BUSID_OTHER;
265 /* release the busid lock */
266 put_busid_priv(bid);
237 267
238 ret = do_rebind((char *) buf, bid); 268 ret = do_rebind((char *) buf, bid);
239 if (ret < 0) 269 if (ret < 0)