Skip to content

Commit 2207655

Browse files
Shuah Khan (Samsung OSG)gregkh
authored andcommitted
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) <[email protected]> Cc: stable <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 7510df3 commit 2207655

File tree

3 files changed

+60
-15
lines changed

3 files changed

+60
-15
lines changed

drivers/usb/usbip/stub.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ struct bus_id_priv {
7373
struct stub_device *sdev;
7474
struct usb_device *udev;
7575
char shutdown_busid;
76+
spinlock_t busid_lock;
7677
};
7778

7879
/* stub_priv is allocated from stub_priv_cache */
@@ -83,6 +84,7 @@ extern struct usb_device_driver stub_driver;
8384

8485
/* stub_main.c */
8586
struct bus_id_priv *get_busid_priv(const char *busid);
87+
void put_busid_priv(struct bus_id_priv *bid);
8688
int del_match_busid(char *busid);
8789
void stub_device_cleanup_urbs(struct stub_device *sdev);
8890

drivers/usb/usbip/stub_dev.c

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ static int stub_probe(struct usb_device *udev)
300300
struct stub_device *sdev = NULL;
301301
const char *udev_busid = dev_name(&udev->dev);
302302
struct bus_id_priv *busid_priv;
303-
int rc;
303+
int rc = 0;
304304

305305
dev_dbg(&udev->dev, "Enter probe\n");
306306

@@ -317,27 +317,32 @@ static int stub_probe(struct usb_device *udev)
317317
* other matched drivers by the driver core.
318318
* See driver_probe_device() in driver/base/dd.c
319319
*/
320-
return -ENODEV;
320+
rc = -ENODEV;
321+
goto call_put_busid_priv;
321322
}
322323

323324
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
324325
dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
325326
udev_busid);
326-
return -ENODEV;
327+
rc = -ENODEV;
328+
goto call_put_busid_priv;
327329
}
328330

329331
if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
330332
dev_dbg(&udev->dev,
331333
"%s is attached on vhci_hcd... skip!\n",
332334
udev_busid);
333335

334-
return -ENODEV;
336+
rc = -ENODEV;
337+
goto call_put_busid_priv;
335338
}
336339

337340
/* ok, this is my device */
338341
sdev = stub_device_alloc(udev);
339-
if (!sdev)
340-
return -ENOMEM;
342+
if (!sdev) {
343+
rc = -ENOMEM;
344+
goto call_put_busid_priv;
345+
}
341346

342347
dev_info(&udev->dev,
343348
"usbip-host: register new device (bus %u dev %u)\n",
@@ -369,7 +374,9 @@ static int stub_probe(struct usb_device *udev)
369374
}
370375
busid_priv->status = STUB_BUSID_ALLOC;
371376

372-
return 0;
377+
rc = 0;
378+
goto call_put_busid_priv;
379+
373380
err_files:
374381
usb_hub_release_port(udev->parent, udev->portnum,
375382
(struct usb_dev_state *) udev);
@@ -379,6 +386,9 @@ static int stub_probe(struct usb_device *udev)
379386

380387
busid_priv->sdev = NULL;
381388
stub_device_free(sdev);
389+
390+
call_put_busid_priv:
391+
put_busid_priv(busid_priv);
382392
return rc;
383393
}
384394

@@ -417,7 +427,7 @@ static void stub_disconnect(struct usb_device *udev)
417427
/* get stub_device */
418428
if (!sdev) {
419429
dev_err(&udev->dev, "could not get device");
420-
return;
430+
goto call_put_busid_priv;
421431
}
422432

423433
dev_set_drvdata(&udev->dev, NULL);
@@ -432,12 +442,12 @@ static void stub_disconnect(struct usb_device *udev)
432442
(struct usb_dev_state *) udev);
433443
if (rc) {
434444
dev_dbg(&udev->dev, "unable to release port\n");
435-
return;
445+
goto call_put_busid_priv;
436446
}
437447

438448
/* If usb reset is called from event handler */
439449
if (usbip_in_eh(current))
440-
return;
450+
goto call_put_busid_priv;
441451

442452
/* shutdown the current connection */
443453
shutdown_busid(busid_priv);
@@ -450,6 +460,9 @@ static void stub_disconnect(struct usb_device *udev)
450460

451461
if (busid_priv->status == STUB_BUSID_ALLOC)
452462
busid_priv->status = STUB_BUSID_ADDED;
463+
464+
call_put_busid_priv:
465+
put_busid_priv(busid_priv);
453466
}
454467

455468
#ifdef CONFIG_PM

drivers/usb/usbip/stub_main.c

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,18 @@ static spinlock_t busid_table_lock;
2626

2727
static void init_busid_table(void)
2828
{
29+
int i;
30+
2931
/*
3032
* This also sets the bus_table[i].status to
3133
* STUB_BUSID_OTHER, which is 0.
3234
*/
3335
memset(busid_table, 0, sizeof(busid_table));
3436

3537
spin_lock_init(&busid_table_lock);
38+
39+
for (i = 0; i < MAX_BUSID; i++)
40+
spin_lock_init(&busid_table[i].busid_lock);
3641
}
3742

3843
/*
@@ -44,29 +49,42 @@ static int get_busid_idx(const char *busid)
4449
int i;
4550
int idx = -1;
4651

47-
for (i = 0; i < MAX_BUSID; i++)
52+
for (i = 0; i < MAX_BUSID; i++) {
53+
spin_lock(&busid_table[i].busid_lock);
4854
if (busid_table[i].name[0])
4955
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
5056
idx = i;
57+
spin_unlock(&busid_table[i].busid_lock);
5158
break;
5259
}
60+
spin_unlock(&busid_table[i].busid_lock);
61+
}
5362
return idx;
5463
}
5564

65+
/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
5666
struct bus_id_priv *get_busid_priv(const char *busid)
5767
{
5868
int idx;
5969
struct bus_id_priv *bid = NULL;
6070

6171
spin_lock(&busid_table_lock);
6272
idx = get_busid_idx(busid);
63-
if (idx >= 0)
73+
if (idx >= 0) {
6474
bid = &(busid_table[idx]);
75+
/* get busid_lock before returning */
76+
spin_lock(&bid->busid_lock);
77+
}
6578
spin_unlock(&busid_table_lock);
6679

6780
return bid;
6881
}
6982

83+
void put_busid_priv(struct bus_id_priv *bid)
84+
{
85+
spin_unlock(&bid->busid_lock);
86+
}
87+
7088
static int add_match_busid(char *busid)
7189
{
7290
int i;
@@ -79,15 +97,19 @@ static int add_match_busid(char *busid)
7997
goto out;
8098
}
8199

82-
for (i = 0; i < MAX_BUSID; i++)
100+
for (i = 0; i < MAX_BUSID; i++) {
101+
spin_lock(&busid_table[i].busid_lock);
83102
if (!busid_table[i].name[0]) {
84103
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
85104
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
86105
(busid_table[i].status != STUB_BUSID_REMOV))
87106
busid_table[i].status = STUB_BUSID_ADDED;
88107
ret = 0;
108+
spin_unlock(&busid_table[i].busid_lock);
89109
break;
90110
}
111+
spin_unlock(&busid_table[i].busid_lock);
112+
}
91113

92114
out:
93115
spin_unlock(&busid_table_lock);
@@ -108,13 +130,16 @@ int del_match_busid(char *busid)
108130
/* found */
109131
ret = 0;
110132

133+
spin_lock(&busid_table[idx].busid_lock);
134+
111135
if (busid_table[idx].status == STUB_BUSID_OTHER)
112136
memset(busid_table[idx].name, 0, BUSID_SIZE);
113137

114138
if ((busid_table[idx].status != STUB_BUSID_OTHER) &&
115139
(busid_table[idx].status != STUB_BUSID_ADDED))
116140
busid_table[idx].status = STUB_BUSID_REMOV;
117141

142+
spin_unlock(&busid_table[idx].busid_lock);
118143
out:
119144
spin_unlock(&busid_table_lock);
120145

@@ -127,9 +152,12 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf)
127152
char *out = buf;
128153

129154
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);
131157
if (busid_table[i].name[0])
132158
out += sprintf(out, "%s ", busid_table[i].name);
159+
spin_unlock(&busid_table[i].busid_lock);
160+
}
133161
spin_unlock(&busid_table_lock);
134162
out += sprintf(out, "\n");
135163

@@ -204,7 +232,7 @@ static void stub_device_rebind(void)
204232
}
205233
spin_unlock(&busid_table_lock);
206234

207-
/* now run rebind */
235+
/* now run rebind - no need to hold locks. driver files are removed */
208236
for (i = 0; i < MAX_BUSID; i++) {
209237
if (busid_table[i].name[0] &&
210238
busid_table[i].shutdown_busid) {
@@ -234,6 +262,8 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
234262

235263
/* mark the device for deletion so probe ignores it during rescan */
236264
bid->status = STUB_BUSID_OTHER;
265+
/* release the busid lock */
266+
put_busid_priv(bid);
237267

238268
ret = do_rebind((char *) buf, bid);
239269
if (ret < 0)

0 commit comments

Comments
 (0)