NetBSD-Bugs archive

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

kern/44907: crash due to race in ehci.c



>Number:         44907
>Category:       kern
>Synopsis:       crash due to race in ehci.c
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Apr 26 21:20:01 +0000 2011
>Originator:     Gordon McNutt
>Release:        5.1
>Organization:
CradlePoint, Inc
>Environment:
NetBSD  5.1 NetBSD 5.1 (SR) #8: Tue Apr 26 15:01:01 MDT 2011  
gmcnutt@gmcnutt-desktop:/home/gmcnutt/svn/cpbsd/trunk/sys/arch/evbmips/compile/obj/SR
 evbmips
#
>Description:
There is a race condition in ehci.c between ehci_timeout and callers of
ehci_freex/ehci_allocx:

1. ehci_timeout() calls usb_add_task(), enqueueing xfer.abort_task
2. someone calls ehci_freex on the same xfer (its abort_task remains enqueued)
3. someone calls ehci_allocx and gets the same xfer, wiping it to zero
4. usb_task_thread subsequently runs and tries to use TAILQ_REMOVE(), which
   results in a 0 pointer deref

Yep, it really happens. The following suggested patch fixes it by always
initializing the abort_task in ehci_allocx and always removing it in
ehci_freex.
>How-To-Repeat:
I run lots of torrents concurrently over multiple usb modems. A couple of usb 
hubs are in the tree as well.
>Fix:
Index: sys/dev/usb/ehci.c
===================================================================
--- sys/dev/usb/ehci.c  (revision 4123)
+++ sys/dev/usb/ehci.c  (working copy)
@@ -1298,7 +1299,9 @@
                xfer = malloc(sizeof(struct ehci_xfer), M_USB, M_NOWAIT);
        }
        if (xfer != NULL) {
+               struct ehci_xfer *exfer = EXFER(xfer);
                memset(xfer, 0, sizeof(struct ehci_xfer));
+               usb_init_task(&exfer->abort_task, ehci_timeout_task, exfer);
 #ifdef DIAGNOSTIC
                EXFER(xfer)->isdone = 1;
                xfer->busy_free = XFER_BUSY;
@@ -1311,6 +1314,7 @@
 ehci_freex(struct usbd_bus *bus, usbd_xfer_handle xfer)
 {
        struct ehci_softc *sc = bus->hci_private;
+       struct ehci_xfer *exfer = EXFER(xfer);
 
 #ifdef DIAGNOSTIC
        if (xfer->busy_free != XFER_BUSY) {
@@ -1322,6 +1326,7 @@
                printf("ehci_freex: !isdone\n");
        }
 #endif
+       usb_rem_task(xfer->pipe->device, &exfer->abort_task);
        SIMPLEQ_INSERT_HEAD(&sc->sc_free_xfers, xfer, next);
 }
 
@@ -3115,7 +3120,6 @@
        }
 
        /* Execute the abort in a process context. */
-       usb_init_task(&exfer->abort_task, ehci_timeout_task, addr);
        usb_add_task(exfer->xfer.pipe->device, &exfer->abort_task,
            USB_TASKQ_HC);
 }



Home | Main Index | Thread Index | Old Index