USB: Remove races in devio.c
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> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
8377c94f62
commit
4e09dcf20f
|
@ -333,17 +333,14 @@ static struct async *async_getcompleted(struct dev_state *ps)
|
||||||
static struct async *async_getpending(struct dev_state *ps,
|
static struct async *async_getpending(struct dev_state *ps,
|
||||||
void __user *userurb)
|
void __user *userurb)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
|
||||||
struct async *as;
|
struct async *as;
|
||||||
|
|
||||||
spin_lock_irqsave(&ps->lock, flags);
|
|
||||||
list_for_each_entry(as, &ps->async_pending, asynclist)
|
list_for_each_entry(as, &ps->async_pending, asynclist)
|
||||||
if (as->userurb == userurb) {
|
if (as->userurb == userurb) {
|
||||||
list_del_init(&as->asynclist);
|
list_del_init(&as->asynclist);
|
||||||
spin_unlock_irqrestore(&ps->lock, flags);
|
|
||||||
return as;
|
return as;
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ps->lock, flags);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,6 +395,7 @@ static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
|
||||||
__releases(ps->lock)
|
__releases(ps->lock)
|
||||||
__acquires(ps->lock)
|
__acquires(ps->lock)
|
||||||
{
|
{
|
||||||
|
struct urb *urb;
|
||||||
struct async *as;
|
struct async *as;
|
||||||
|
|
||||||
/* Mark all the pending URBs that match bulk_addr, up to but not
|
/* Mark all the pending URBs that match bulk_addr, up to but not
|
||||||
|
@ -420,8 +418,11 @@ __acquires(ps->lock)
|
||||||
list_for_each_entry(as, &ps->async_pending, asynclist) {
|
list_for_each_entry(as, &ps->async_pending, asynclist) {
|
||||||
if (as->bulk_status == AS_UNLINK) {
|
if (as->bulk_status == AS_UNLINK) {
|
||||||
as->bulk_status = 0; /* Only once */
|
as->bulk_status = 0; /* Only once */
|
||||||
|
urb = as->urb;
|
||||||
|
usb_get_urb(urb);
|
||||||
spin_unlock(&ps->lock); /* Allow completions */
|
spin_unlock(&ps->lock); /* Allow completions */
|
||||||
usb_unlink_urb(as->urb);
|
usb_unlink_urb(urb);
|
||||||
|
usb_put_urb(urb);
|
||||||
spin_lock(&ps->lock);
|
spin_lock(&ps->lock);
|
||||||
goto rescan;
|
goto rescan;
|
||||||
}
|
}
|
||||||
|
@ -472,6 +473,7 @@ static void async_completed(struct urb *urb)
|
||||||
|
|
||||||
static void destroy_async(struct dev_state *ps, struct list_head *list)
|
static void destroy_async(struct dev_state *ps, struct list_head *list)
|
||||||
{
|
{
|
||||||
|
struct urb *urb;
|
||||||
struct async *as;
|
struct async *as;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
|
@ -479,10 +481,13 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
|
||||||
while (!list_empty(list)) {
|
while (!list_empty(list)) {
|
||||||
as = list_entry(list->next, struct async, asynclist);
|
as = list_entry(list->next, struct async, asynclist);
|
||||||
list_del_init(&as->asynclist);
|
list_del_init(&as->asynclist);
|
||||||
|
urb = as->urb;
|
||||||
|
usb_get_urb(urb);
|
||||||
|
|
||||||
/* drop the spinlock so the completion handler can run */
|
/* drop the spinlock so the completion handler can run */
|
||||||
spin_unlock_irqrestore(&ps->lock, flags);
|
spin_unlock_irqrestore(&ps->lock, flags);
|
||||||
usb_kill_urb(as->urb);
|
usb_kill_urb(urb);
|
||||||
|
usb_put_urb(urb);
|
||||||
spin_lock_irqsave(&ps->lock, flags);
|
spin_lock_irqsave(&ps->lock, flags);
|
||||||
}
|
}
|
||||||
spin_unlock_irqrestore(&ps->lock, flags);
|
spin_unlock_irqrestore(&ps->lock, flags);
|
||||||
|
@ -1399,12 +1404,24 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)
|
||||||
|
|
||||||
static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
|
static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
|
||||||
{
|
{
|
||||||
|
struct urb *urb;
|
||||||
struct async *as;
|
struct async *as;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&ps->lock, flags);
|
||||||
as = async_getpending(ps, arg);
|
as = async_getpending(ps, arg);
|
||||||
if (!as)
|
if (!as) {
|
||||||
|
spin_unlock_irqrestore(&ps->lock, flags);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
usb_kill_urb(as->urb);
|
}
|
||||||
|
|
||||||
|
urb = as->urb;
|
||||||
|
usb_get_urb(urb);
|
||||||
|
spin_unlock_irqrestore(&ps->lock, flags);
|
||||||
|
|
||||||
|
usb_kill_urb(urb);
|
||||||
|
usb_put_urb(urb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue