Commit 06df5729 authored by Sarah Sharp's avatar Sarah Sharp Committed by Greg Kroah-Hartman

USB: xhci: Fix command completion after a drop endpoint.

The xHCI driver issues a Configure Endpoint command for two reasons:
 - a new configuration or alternate interface setting is selected
 - a quirky Fresco Logic prototype requires the command after a Reset
   Endpoint command.
The xHCI driver only waits on the command in the first case.

When a configure endpoint command completes, the driver needs to know why
the command was generated.  When the driver only supported selecting an
initial configuration, the check was simple.  Unfortunately that check
doesn't work now that the driver supports alternate interfaces.  If an
endpoint must be dropped (because it's not in the new alternate setting)
and no new endpoints are added, the math involving
xhci_last_valid_endpoint() will assign -1 to an unsigned integer and cause
an out-of-bounds array access.

Move the check for the quirky hardware sooner and avoid the bad array
access.
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 74f9fe21
...@@ -903,28 +903,32 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ...@@ -903,28 +903,32 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
virt_dev->in_ctx); virt_dev->in_ctx);
/* Input ctx add_flags are the endpoint index plus one */ /* Input ctx add_flags are the endpoint index plus one */
ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1; ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1;
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; /* A usb_set_interface() call directly after clearing a halted
if (!ep_ring) { * condition may race on this quirky hardware.
/* This must have been an initial configure endpoint */ * Not worth worrying about, since this is prototype hardware.
xhci->devs[slot_id]->cmd_status = */
GET_COMP_CODE(event->status);
complete(&xhci->devs[slot_id]->cmd_completion);
break;
}
ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
xhci_dbg(xhci, "Completed config ep cmd - last ep index = %d, "
"state = %d\n", ep_index, ep_state);
if (xhci->quirks & XHCI_RESET_EP_QUIRK && if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
ep_state & EP_HALTED) { ep_index != (unsigned int) -1 &&
ctrl_ctx->add_flags - SLOT_FLAG ==
ctrl_ctx->drop_flags) {
ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
if (!(ep_state & EP_HALTED))
goto bandwidth_change;
xhci_dbg(xhci, "Completed config ep cmd - "
"last ep index = %d, state = %d\n",
ep_index, ep_state);
/* Clear our internal halted state and restart ring */ /* Clear our internal halted state and restart ring */
xhci->devs[slot_id]->eps[ep_index].ep_state &= xhci->devs[slot_id]->eps[ep_index].ep_state &=
~EP_HALTED; ~EP_HALTED;
ring_ep_doorbell(xhci, slot_id, ep_index); ring_ep_doorbell(xhci, slot_id, ep_index);
} else { break;
xhci->devs[slot_id]->cmd_status =
GET_COMP_CODE(event->status);
complete(&xhci->devs[slot_id]->cmd_completion);
} }
bandwidth_change:
xhci_dbg(xhci, "Completed config ep cmd\n");
xhci->devs[slot_id]->cmd_status =
GET_COMP_CODE(event->status);
complete(&xhci->devs[slot_id]->cmd_completion);
break; break;
case TRB_TYPE(TRB_EVAL_CONTEXT): case TRB_TYPE(TRB_EVAL_CONTEXT):
virt_dev = xhci->devs[slot_id]; virt_dev = xhci->devs[slot_id];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment