aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHuajun Li <huajun.li.lee@gmail.com>2012-05-18 08:12:51 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-06-01 03:12:57 -0400
commit87d8d621cdd1c47d0dc19118081412d3f8178e72 (patch)
treebdd07621614e45a0b56927507e26af3619bba145
parent2960d811d562540494c83b96e4ae4b6b11196016 (diff)
USB: Remove races in devio.c
commit 4e09dcf20f7b5358615514c2ec8584b248ab8874 upstream. There exist races in devio.c, below is one case, and there are similar races in destroy_async() and proc_unlinkurb(). Remove these races. cancel_bulk_urbs() async_completed() ------------------- ----------------------- spin_unlock(&ps->lock); list_move_tail(&as->asynclist, &ps->async_completed); wake_up(&ps->wait); Lead to free_async() be triggered, then urb and 'as' will be freed. usb_unlink_urb(as->urb); ===> refer to the freed 'as' Signed-off-by: Huajun Li <huajun.li.lee@gmail.com> Cc: Alan Stern <stern@rowland.harvard.edu> Cc: Oncaphillis <oncaphillis@snafu.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/devio.c33
1 files changed, 25 insertions, 8 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 0ca54e22d31..ca3c303eed8 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -292,17 +292,14 @@ static struct async *async_getcompleted(struct dev_state *ps)
292static struct async *async_getpending(struct dev_state *ps, 292static struct async *async_getpending(struct dev_state *ps,
293 void __user *userurb) 293 void __user *userurb)
294{ 294{
295 unsigned long flags;
296 struct async *as; 295 struct async *as;
297 296
298 spin_lock_irqsave(&ps->lock, flags);
299 list_for_each_entry(as, &ps->async_pending, asynclist) 297 list_for_each_entry(as, &ps->async_pending, asynclist)
300 if (as->userurb == userurb) { 298 if (as->userurb == userurb) {
301 list_del_init(&as->asynclist); 299 list_del_init(&as->asynclist);
302 spin_unlock_irqrestore(&ps->lock, flags);
303 return as; 300 return as;
304 } 301 }
305 spin_unlock_irqrestore(&ps->lock, flags); 302
306 return NULL; 303 return NULL;
307} 304}
308 305
@@ -357,6 +354,7 @@ static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
357__releases(ps->lock) 354__releases(ps->lock)
358__acquires(ps->lock) 355__acquires(ps->lock)
359{ 356{
357 struct urb *urb;
360 struct async *as; 358 struct async *as;
361 359
362 /* Mark all the pending URBs that match bulk_addr, up to but not 360 /* Mark all the pending URBs that match bulk_addr, up to but not
@@ -379,8 +377,11 @@ __acquires(ps->lock)
379 list_for_each_entry(as, &ps->async_pending, asynclist) { 377 list_for_each_entry(as, &ps->async_pending, asynclist) {
380 if (as->bulk_status == AS_UNLINK) { 378 if (as->bulk_status == AS_UNLINK) {
381 as->bulk_status = 0; /* Only once */ 379 as->bulk_status = 0; /* Only once */
380 urb = as->urb;
381 usb_get_urb(urb);
382 spin_unlock(&ps->lock); /* Allow completions */ 382 spin_unlock(&ps->lock); /* Allow completions */
383 usb_unlink_urb(as->urb); 383 usb_unlink_urb(urb);
384 usb_put_urb(urb);
384 spin_lock(&ps->lock); 385 spin_lock(&ps->lock);
385 goto rescan; 386 goto rescan;
386 } 387 }
@@ -433,6 +434,7 @@ static void async_completed(struct urb *urb)
433 434
434static void destroy_async(struct dev_state *ps, struct list_head *list) 435static void destroy_async(struct dev_state *ps, struct list_head *list)
435{ 436{
437 struct urb *urb;
436 struct async *as; 438 struct async *as;
437 unsigned long flags; 439 unsigned long flags;
438 440
@@ -440,10 +442,13 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
440 while (!list_empty(list)) { 442 while (!list_empty(list)) {
441 as = list_entry(list->next, struct async, asynclist); 443 as = list_entry(list->next, struct async, asynclist);
442 list_del_init(&as->asynclist); 444 list_del_init(&as->asynclist);
445 urb = as->urb;
446 usb_get_urb(urb);
443 447
444 /* drop the spinlock so the completion handler can run */ 448 /* drop the spinlock so the completion handler can run */
445 spin_unlock_irqrestore(&ps->lock, flags); 449 spin_unlock_irqrestore(&ps->lock, flags);
446 usb_kill_urb(as->urb); 450 usb_kill_urb(urb);
451 usb_put_urb(urb);
447 spin_lock_irqsave(&ps->lock, flags); 452 spin_lock_irqsave(&ps->lock, flags);
448 } 453 }
449 spin_unlock_irqrestore(&ps->lock, flags); 454 spin_unlock_irqrestore(&ps->lock, flags);
@@ -1352,12 +1357,24 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)
1352 1357
1353static int proc_unlinkurb(struct dev_state *ps, void __user *arg) 1358static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
1354{ 1359{
1360 struct urb *urb;
1355 struct async *as; 1361 struct async *as;
1362 unsigned long flags;
1356 1363
1364 spin_lock_irqsave(&ps->lock, flags);
1357 as = async_getpending(ps, arg); 1365 as = async_getpending(ps, arg);
1358 if (!as) 1366 if (!as) {
1367 spin_unlock_irqrestore(&ps->lock, flags);
1359 return -EINVAL; 1368 return -EINVAL;
1360 usb_kill_urb(as->urb); 1369 }
1370
1371 urb = as->urb;
1372 usb_get_urb(urb);
1373 spin_unlock_irqrestore(&ps->lock, flags);
1374
1375 usb_kill_urb(urb);
1376 usb_put_urb(urb);
1377
1361 return 0; 1378 return 0;
1362} 1379}
1363 1380