Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4e09dcf2 authored by Huajun Li's avatar Huajun Li Committed by Greg Kroah-Hartman
Browse files

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: default avatarHuajun 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: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 8377c94f
Loading
Loading
Loading
Loading
+25 −8
Original line number Original line Diff line number Diff line
@@ -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;
}
}