Skip to content

Commit edaa42e

Browse files
Shuah Khan (Samsung OSG)ZhengShunQian
authored andcommitted
usbip: usbip_host: fix NULL-ptr deref and use-after-free errors
commit 2207655 upstream. 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 60faafd commit edaa42e

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
@@ -88,6 +88,7 @@ struct bus_id_priv {
8888
struct stub_device *sdev;
8989
struct usb_device *udev;
9090
char shutdown_busid;
91+
spinlock_t busid_lock;
9192
};
9293

9394
/* stub_priv is allocated from stub_priv_cache */
@@ -98,6 +99,7 @@ extern struct usb_device_driver stub_driver;
9899

99100
/* stub_main.c */
100101
struct bus_id_priv *get_busid_priv(const char *busid);
102+
void put_busid_priv(struct bus_id_priv *bid);
101103
int del_match_busid(char *busid);
102104
void stub_device_cleanup_urbs(struct stub_device *sdev);
103105

drivers/usb/usbip/stub_dev.c

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ static int stub_probe(struct usb_device *udev)
314314
struct stub_device *sdev = NULL;
315315
const char *udev_busid = dev_name(&udev->dev);
316316
struct bus_id_priv *busid_priv;
317-
int rc;
317+
int rc = 0;
318318

319319
dev_dbg(&udev->dev, "Enter probe\n");
320320

@@ -331,27 +331,32 @@ static int stub_probe(struct usb_device *udev)
331331
* other matched drivers by the driver core.
332332
* See driver_probe_device() in driver/base/dd.c
333333
*/
334-
return -ENODEV;
334+
rc = -ENODEV;
335+
goto call_put_busid_priv;
335336
}
336337

337338
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) {
338339
dev_dbg(&udev->dev, "%s is a usb hub device... skip!\n",
339340
udev_busid);
340-
return -ENODEV;
341+
rc = -ENODEV;
342+
goto call_put_busid_priv;
341343
}
342344

343345
if (!strcmp(udev->bus->bus_name, "vhci_hcd")) {
344346
dev_dbg(&udev->dev,
345347
"%s is attached on vhci_hcd... skip!\n",
346348
udev_busid);
347349

348-
return -ENODEV;
350+
rc = -ENODEV;
351+
goto call_put_busid_priv;
349352
}
350353

351354
/* ok, this is my device */
352355
sdev = stub_device_alloc(udev);
353-
if (!sdev)
354-
return -ENOMEM;
356+
if (!sdev) {
357+
rc = -ENOMEM;
358+
goto call_put_busid_priv;
359+
}
355360

356361
dev_info(&udev->dev,
357362
"usbip-host: register new device (bus %u dev %u)\n",
@@ -383,7 +388,9 @@ static int stub_probe(struct usb_device *udev)
383388
}
384389
busid_priv->status = STUB_BUSID_ALLOC;
385390

386-
return 0;
391+
rc = 0;
392+
goto call_put_busid_priv;
393+
387394
err_files:
388395
usb_hub_release_port(udev->parent, udev->portnum,
389396
(struct usb_dev_state *) udev);
@@ -394,6 +401,9 @@ static int stub_probe(struct usb_device *udev)
394401

395402
busid_priv->sdev = NULL;
396403
stub_device_free(sdev);
404+
405+
call_put_busid_priv:
406+
put_busid_priv(busid_priv);
397407
return rc;
398408
}
399409

@@ -432,7 +442,7 @@ static void stub_disconnect(struct usb_device *udev)
432442
/* get stub_device */
433443
if (!sdev) {
434444
dev_err(&udev->dev, "could not get device");
435-
return;
445+
goto call_put_busid_priv;
436446
}
437447

438448
dev_set_drvdata(&udev->dev, NULL);
@@ -447,12 +457,12 @@ static void stub_disconnect(struct usb_device *udev)
447457
(struct usb_dev_state *) udev);
448458
if (rc) {
449459
dev_dbg(&udev->dev, "unable to release port\n");
450-
return;
460+
goto call_put_busid_priv;
451461
}
452462

453463
/* If usb reset is called from event handler */
454464
if (busid_priv->sdev->ud.eh == current)
455-
return;
465+
goto call_put_busid_priv;
456466

457467
/* shutdown the current connection */
458468
shutdown_busid(busid_priv);
@@ -465,6 +475,9 @@ static void stub_disconnect(struct usb_device *udev)
465475

466476
if (busid_priv->status == STUB_BUSID_ALLOC)
467477
busid_priv->status = STUB_BUSID_ADDED;
478+
479+
call_put_busid_priv:
480+
put_busid_priv(busid_priv);
468481
}
469482

470483
#ifdef CONFIG_PM

drivers/usb/usbip/stub_main.c

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

4141
static void init_busid_table(void)
4242
{
43+
int i;
44+
4345
/*
4446
* This also sets the bus_table[i].status to
4547
* STUB_BUSID_OTHER, which is 0.
4648
*/
4749
memset(busid_table, 0, sizeof(busid_table));
4850

4951
spin_lock_init(&busid_table_lock);
52+
53+
for (i = 0; i < MAX_BUSID; i++)
54+
spin_lock_init(&busid_table[i].busid_lock);
5055
}
5156

5257
/*
@@ -58,29 +63,42 @@ static int get_busid_idx(const char *busid)
5863
int i;
5964
int idx = -1;
6065

61-
for (i = 0; i < MAX_BUSID; i++)
66+
for (i = 0; i < MAX_BUSID; i++) {
67+
spin_lock(&busid_table[i].busid_lock);
6268
if (busid_table[i].name[0])
6369
if (!strncmp(busid_table[i].name, busid, BUSID_SIZE)) {
6470
idx = i;
71+
spin_unlock(&busid_table[i].busid_lock);
6572
break;
6673
}
74+
spin_unlock(&busid_table[i].busid_lock);
75+
}
6776
return idx;
6877
}
6978

79+
/* Returns holding busid_lock. Should call put_busid_priv() to unlock */
7080
struct bus_id_priv *get_busid_priv(const char *busid)
7181
{
7282
int idx;
7383
struct bus_id_priv *bid = NULL;
7484

7585
spin_lock(&busid_table_lock);
7686
idx = get_busid_idx(busid);
77-
if (idx >= 0)
87+
if (idx >= 0) {
7888
bid = &(busid_table[idx]);
89+
/* get busid_lock before returning */
90+
spin_lock(&bid->busid_lock);
91+
}
7992
spin_unlock(&busid_table_lock);
8093

8194
return bid;
8295
}
8396

97+
void put_busid_priv(struct bus_id_priv *bid)
98+
{
99+
spin_unlock(&bid->busid_lock);
100+
}
101+
84102
static int add_match_busid(char *busid)
85103
{
86104
int i;
@@ -93,15 +111,19 @@ static int add_match_busid(char *busid)
93111
goto out;
94112
}
95113

96-
for (i = 0; i < MAX_BUSID; i++)
114+
for (i = 0; i < MAX_BUSID; i++) {
115+
spin_lock(&busid_table[i].busid_lock);
97116
if (!busid_table[i].name[0]) {
98117
strlcpy(busid_table[i].name, busid, BUSID_SIZE);
99118
if ((busid_table[i].status != STUB_BUSID_ALLOC) &&
100119
(busid_table[i].status != STUB_BUSID_REMOV))
101120
busid_table[i].status = STUB_BUSID_ADDED;
102121
ret = 0;
122+
spin_unlock(&busid_table[i].busid_lock);
103123
break;
104124
}
125+
spin_unlock(&busid_table[i].busid_lock);
126+
}
105127

106128
out:
107129
spin_unlock(&busid_table_lock);
@@ -122,13 +144,16 @@ int del_match_busid(char *busid)
122144
/* found */
123145
ret = 0;
124146

147+
spin_lock(&busid_table[idx].busid_lock);
148+
125149
if (busid_table[idx].status == STUB_BUSID_OTHER)
126150
memset(busid_table[idx].name, 0, BUSID_SIZE);
127151

128152
if ((busid_table[idx].status != STUB_BUSID_OTHER) &&
129153
(busid_table[idx].status != STUB_BUSID_ADDED))
130154
busid_table[idx].status = STUB_BUSID_REMOV;
131155

156+
spin_unlock(&busid_table[idx].busid_lock);
132157
out:
133158
spin_unlock(&busid_table_lock);
134159

@@ -141,9 +166,12 @@ static ssize_t show_match_busid(struct device_driver *drv, char *buf)
141166
char *out = buf;
142167

143168
spin_lock(&busid_table_lock);
144-
for (i = 0; i < MAX_BUSID; i++)
169+
for (i = 0; i < MAX_BUSID; i++) {
170+
spin_lock(&busid_table[i].busid_lock);
145171
if (busid_table[i].name[0])
146172
out += sprintf(out, "%s ", busid_table[i].name);
173+
spin_unlock(&busid_table[i].busid_lock);
174+
}
147175
spin_unlock(&busid_table_lock);
148176
out += sprintf(out, "\n");
149177

@@ -219,7 +247,7 @@ static void stub_device_rebind(void)
219247
}
220248
spin_unlock(&busid_table_lock);
221249

222-
/* now run rebind */
250+
/* now run rebind - no need to hold locks. driver files are removed */
223251
for (i = 0; i < MAX_BUSID; i++) {
224252
if (busid_table[i].name[0] &&
225253
busid_table[i].shutdown_busid) {
@@ -249,6 +277,8 @@ static ssize_t rebind_store(struct device_driver *dev, const char *buf,
249277

250278
/* mark the device for deletion so probe ignores it during rescan */
251279
bid->status = STUB_BUSID_OTHER;
280+
/* release the busid lock */
281+
put_busid_priv(bid);
252282

253283
ret = do_rebind((char *) buf, bid);
254284
if (ret < 0)

0 commit comments

Comments
 (0)