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.pugli...@gmail.com>
---
 drivers/usb/wusbcore/wa-xfer.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index c688afa..1cd0f61 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)
                goto out_unlock;        /* setup(), enqueue_b() completes */
        /* Ok, the xfer is in flight already, it's been setup and submitted.*/
        xfer_abort_pending = __wa_xfer_abort(xfer) >= 0;
+       /*
+        * grab the rpipe->seg_lock here to prevent racing with
+        * __wa_xfer_delayed_run.
+        */
+       spin_lock(&rpipe->seg_lock);
        for (cnt = 0; cnt < xfer->segs; cnt++) {
                seg = xfer->seg[cnt];
                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)
                         */
                        seg->status = WA_SEG_ABORTED;
                        seg->result = -ENOENT;
-                       spin_lock_irqsave(&rpipe->seg_lock, flags2);
                        list_del(&seg->list_node);
                        xfer->segs_done++;
-                       spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
                        break;
                case WA_SEG_DONE:
                case WA_SEG_ERROR:
@@ -2026,6 +2029,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int 
status)
                        break;
                }
        }
+       spin_unlock(&rpipe->seg_lock);
        xfer->result = urb->status;     /* -ENOENT or -ECONNRESET */
        done = __wa_xfer_is_done(xfer);
        spin_unlock_irqrestore(&xfer->lock, flags);
-- 
1.8.3.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to