Re: SLUB Corruption from witin drivers/usb/core/devio.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 05/15/12 07:55pm, Oncaphillis wrote:
>On 05/16/2012 02:56 AM, Huajun Li wrote:
>>Thanks for your reminder, and here is the draft patch for review:
>
>Didn't do the trck. I patched 3.3.4 .. Should i use  3.4-rc7 ?
>

>From 3.3.4 to 3.4-rc7, devio.c has no much changes, maybe this is
not the root cause of your problem.

However, I think there still exists another race in destroy_async_on_interface(),
Care to try another patch, or could you please share a piece of codes which
may reproduce the issue?

===========================================================
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 8df4b76..1c16d0a 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -64,6 +64,7 @@ struct dev_state {
 	struct usb_device *dev;
 	struct file *file;
 	spinlock_t lock;            /* protects the async urb lists */
+	struct list_head async_intf;
 	struct list_head async_pending;
 	struct list_head async_completed;
 	wait_queue_head_t wait;     /* wake up if a request completed */
@@ -333,17 +334,14 @@ static struct async *async_getcompleted(struct dev_state *ps)
 static struct async *async_getpending(struct dev_state *ps,
 					     void __user *userurb)
 {
-	unsigned long flags;
 	struct async *as;
 
-	spin_lock_irqsave(&ps->lock, flags);
 	list_for_each_entry(as, &ps->async_pending, asynclist)
 		if (as->userurb == userurb) {
 			list_del_init(&as->asynclist);
-			spin_unlock_irqrestore(&ps->lock, flags);
 			return as;
 		}
-	spin_unlock_irqrestore(&ps->lock, flags);
+
 	return NULL;
 }
 
@@ -398,6 +396,7 @@ static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
 __releases(ps->lock)
 __acquires(ps->lock)
 {
+	struct urb *urb;
 	struct async *as;
 
 	/* Mark all the pending URBs that match bulk_addr, up to but not
@@ -420,8 +419,11 @@ __acquires(ps->lock)
 	list_for_each_entry(as, &ps->async_pending, asynclist) {
 		if (as->bulk_status == AS_UNLINK) {
 			as->bulk_status = 0;		/* Only once */
+			urb = as->urb;
+			usb_get_urb(urb);
 			spin_unlock(&ps->lock);		/* Allow completions */
-			usb_unlink_urb(as->urb);
+			usb_unlink_urb(urb);
+			usb_put_urb(urb);
 			spin_lock(&ps->lock);
 			goto rescan;
 		}
@@ -472,6 +474,7 @@ static void async_completed(struct urb *urb)
 
 static void destroy_async(struct dev_state *ps, struct list_head *list)
 {
+	struct urb *urb;
 	struct async *as;
 	unsigned long flags;
 
@@ -479,10 +482,13 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
 	while (!list_empty(list)) {
 		as = list_entry(list->next, struct async, asynclist);
 		list_del_init(&as->asynclist);
+		urb = as->urb;
+		usb_get_urb(urb);
 
 		/* drop the spinlock so the completion handler can run */
 		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_unlock_irqrestore(&ps->lock, flags);
@@ -491,21 +497,22 @@ static void destroy_async(struct dev_state *ps, struct list_head *list)
 static void destroy_async_on_interface(struct dev_state *ps,
 				       unsigned int ifnum)
 {
-	struct list_head *p, *q, hitlist;
+	struct list_head *p, *q;
 	unsigned long flags;
 
-	INIT_LIST_HEAD(&hitlist);
+	INIT_LIST_HEAD(&ps->async_intf);
 	spin_lock_irqsave(&ps->lock, flags);
 	list_for_each_safe(p, q, &ps->async_pending)
 		if (ifnum == list_entry(p, struct async, asynclist)->ifnum)
-			list_move_tail(p, &hitlist);
+			list_move_tail(p, &ps->async_intf);
 	spin_unlock_irqrestore(&ps->lock, flags);
-	destroy_async(ps, &hitlist);
+	destroy_async(ps, &ps->async_intf);
 }
 
 static void destroy_all_async(struct dev_state *ps)
 {
 	destroy_async(ps, &ps->async_pending);
+	destroy_async(ps, &ps->async_intf);
 }
 
 /*
@@ -1410,12 +1417,24 @@ static int proc_submiturb(struct dev_state *ps, void __user *arg)
 
 static int proc_unlinkurb(struct dev_state *ps, void __user *arg)
 {
+	struct urb *urb;
 	struct async *as;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ps->lock, flags);
 	as = async_getpending(ps, arg);
-	if (!as)
+	if (!as) {
+		spin_unlock_irqrestore(&ps->lock, flags);
 		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;
 }
 
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux