aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-03-24 11:02:58 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-04-13 19:19:49 -0400
commit386139d7c8f22d4983ca89de35d339cc41bb0996 (patch)
treeb17d3deb6538c370854f3e59d2a152004feda75a
parentdfa49c4ad120a784ef1ff0717168aa79f55a483a (diff)
xhci: Fix NULL pointer deref in handle_port_status()
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>
-rw-r--r--drivers/usb/host/xhci-ring.c11
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index b69a0a136e6..b0b4cc3b858 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1235,6 +1235,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
1235 u8 major_revision; 1235 u8 major_revision;
1236 struct xhci_bus_state *bus_state; 1236 struct xhci_bus_state *bus_state;
1237 u32 __iomem **port_array; 1237 u32 __iomem **port_array;
1238 bool bogus_port_status = false;
1238 1239
1239 /* Port status change events always have a successful completion code */ 1240 /* Port status change events always have a successful completion code */
1240 if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) { 1241 if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
@@ -1247,6 +1248,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
1247 max_ports = HCS_MAX_PORTS(xhci->hcs_params1); 1248 max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
1248 if ((port_id <= 0) || (port_id > max_ports)) { 1249 if ((port_id <= 0) || (port_id > max_ports)) {
1249 xhci_warn(xhci, "Invalid port id %d\n", port_id); 1250 xhci_warn(xhci, "Invalid port id %d\n", port_id);
1251 bogus_port_status = true;
1250 goto cleanup; 1252 goto cleanup;
1251 } 1253 }
1252 1254
@@ -1258,12 +1260,14 @@ static void handle_port_status(struct xhci_hcd *xhci,
1258 xhci_warn(xhci, "Event for port %u not in " 1260 xhci_warn(xhci, "Event for port %u not in "
1259 "Extended Capabilities, ignoring.\n", 1261 "Extended Capabilities, ignoring.\n",
1260 port_id); 1262 port_id);
1263 bogus_port_status = true;
1261 goto cleanup; 1264 goto cleanup;
1262 } 1265 }
1263 if (major_revision == DUPLICATE_ENTRY) { 1266 if (major_revision == DUPLICATE_ENTRY) {
1264 xhci_warn(xhci, "Event for port %u duplicated in" 1267 xhci_warn(xhci, "Event for port %u duplicated in"
1265 "Extended Capabilities, ignoring.\n", 1268 "Extended Capabilities, ignoring.\n",
1266 port_id); 1269 port_id);
1270 bogus_port_status = true;
1267 goto cleanup; 1271 goto cleanup;
1268 } 1272 }
1269 1273
@@ -1335,6 +1339,13 @@ cleanup:
1335 /* Update event ring dequeue pointer before dropping the lock */ 1339 /* Update event ring dequeue pointer before dropping the lock */
1336 inc_deq(xhci, xhci->event_ring, true); 1340 inc_deq(xhci, xhci->event_ring, true);
1337 1341
1342 /* Don't make the USB core poll the roothub if we got a bad port status
1343 * change event. Besides, at that point we can't tell which roothub
1344 * (USB 2.0 or USB 3.0) to kick.
1345 */
1346 if (bogus_port_status)
1347 return;
1348
1338 spin_unlock(&xhci->lock); 1349 spin_unlock(&xhci->lock);
1339 /* Pass this up to the core */ 1350 /* Pass this up to the core */
1340 usb_hcd_poll_rh_status(hcd); 1351 usb_hcd_poll_rh_status(hcd);