xhci: Fix NULL pointer deref in handle_port_status()
authorSarah Sharp <sarah.a.sharp@linux.intel.com>
Thu, 24 Mar 2011 15:02:58 +0000 (08:02 -0700)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Wed, 13 Apr 2011 23:19:49 +0000 (16:19 -0700)
When we get a port status change event, we need to figure out what type of
port it came from: a USB 3.0 port, or a USB 2.0/1.1 port.  We can't know
which usb_hcd to use until that point, so hcd will be NULL for part of the
function.  Unfortunately, if any of the sanity checks fail, we'll jump to
the cleanup label before hcd is set to a valid pointer, and then we'll
attempt to tell the USB core to kick the hcd, which is NULL.

Skip kicking the roothub if the sanity checks fail.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
drivers/usb/host/xhci-ring.c

index b69a0a1..b0b4cc3 100644 (file)
@@ -1235,6 +1235,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
        u8 major_revision;
        struct xhci_bus_state *bus_state;
        u32 __iomem **port_array;
+       bool bogus_port_status = false;
 
        /* Port status change events always have a successful completion code */
        if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
@@ -1247,6 +1248,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
        max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
        if ((port_id <= 0) || (port_id > max_ports)) {
                xhci_warn(xhci, "Invalid port id %d\n", port_id);
+               bogus_port_status = true;
                goto cleanup;
        }
 
@@ -1258,12 +1260,14 @@ static void handle_port_status(struct xhci_hcd *xhci,
                xhci_warn(xhci, "Event for port %u not in "
                                "Extended Capabilities, ignoring.\n",
                                port_id);
+               bogus_port_status = true;
                goto cleanup;
        }
        if (major_revision == DUPLICATE_ENTRY) {
                xhci_warn(xhci, "Event for port %u duplicated in"
                                "Extended Capabilities, ignoring.\n",
                                port_id);
+               bogus_port_status = true;
                goto cleanup;
        }
 
@@ -1335,6 +1339,13 @@ cleanup:
        /* Update event ring dequeue pointer before dropping the lock */
        inc_deq(xhci, xhci->event_ring, true);
 
+       /* Don't make the USB core poll the roothub if we got a bad port status
+        * change event.  Besides, at that point we can't tell which roothub
+        * (USB 2.0 or USB 3.0) to kick.
+        */
+       if (bogus_port_status)
+               return;
+
        spin_unlock(&xhci->lock);
        /* Pass this up to the core */
        usb_hcd_poll_rh_status(hcd);