aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/wusbcore
diff options
context:
space:
mode:
authorThomas Pugliese <thomas.pugliese@gmail.com>2014-03-04 12:24:55 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-03-07 15:39:39 -0500
commit2a6da97ff530650d26570a6a1ec0ac1deac927bd (patch)
tree64222edab88045ceda239c12a719fbcafd4889db /drivers/usb/wusbcore
parent3c1b2c3ecd3122fe6dded7b012f74021144d95b2 (diff)
usb: wusbcore: fix potential double list_del on urb dequeue
This patch locks rpipe->seg_lock around the entire transfer segment cleanup loop in wa_urb_dequeue instead of just one case of the switch statement. This fixes a race between __wa_xfer_delayed_run and wa_urb_dequeue where a transfer segment in the WA_SEG_DELAYED state could be removed from the rpipe seg_list twice leading to memory corruption. It also switches the spin_lock call to use the non-irqsave version since the xfer->lock is already held and irqs already disabled. Signed-off-by: Thomas Pugliese <thomas.pugliese@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/wusbcore')
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c8
1 files changed, 6 insertions, 2 deletions
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 404ce81b286c..6e0d377d437c 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -1974,6 +1974,11 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
1974 goto out_unlock; /* setup(), enqueue_b() completes */ 1974 goto out_unlock; /* setup(), enqueue_b() completes */
1975 /* Ok, the xfer is in flight already, it's been setup and submitted.*/ 1975 /* Ok, the xfer is in flight already, it's been setup and submitted.*/
1976 xfer_abort_pending = __wa_xfer_abort(xfer) >= 0; 1976 xfer_abort_pending = __wa_xfer_abort(xfer) >= 0;
1977 /*
1978 * grab the rpipe->seg_lock here to prevent racing with
1979 * __wa_xfer_delayed_run.
1980 */
1981 spin_lock(&rpipe->seg_lock);
1977 for (cnt = 0; cnt < xfer->segs; cnt++) { 1982 for (cnt = 0; cnt < xfer->segs; cnt++) {
1978 seg = xfer->seg[cnt]; 1983 seg = xfer->seg[cnt];
1979 pr_debug("%s: xfer id 0x%08X#%d status = %d\n", 1984 pr_debug("%s: xfer id 0x%08X#%d status = %d\n",
@@ -1994,10 +1999,8 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
1994 */ 1999 */
1995 seg->status = WA_SEG_ABORTED; 2000 seg->status = WA_SEG_ABORTED;
1996 seg->result = -ENOENT; 2001 seg->result = -ENOENT;
1997 spin_lock_irqsave(&rpipe->seg_lock, flags2);
1998 list_del(&seg->list_node); 2002 list_del(&seg->list_node);
1999 xfer->segs_done++; 2003 xfer->segs_done++;
2000 spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
2001 break; 2004 break;
2002 case WA_SEG_DONE: 2005 case WA_SEG_DONE:
2003 case WA_SEG_ERROR: 2006 case WA_SEG_ERROR:
@@ -2026,6 +2029,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
2026 break; 2029 break;
2027 } 2030 }
2028 } 2031 }
2032 spin_unlock(&rpipe->seg_lock);
2029 xfer->result = urb->status; /* -ENOENT or -ECONNRESET */ 2033 xfer->result = urb->status; /* -ENOENT or -ECONNRESET */
2030 done = __wa_xfer_is_done(xfer); 2034 done = __wa_xfer_is_done(xfer);
2031 spin_unlock_irqrestore(&xfer->lock, flags); 2035 spin_unlock_irqrestore(&xfer->lock, flags);