mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-04 16:15:11 +00:00
xhci: fix possible null pointer deref during xhci urb enqueue
There is a short gap between urb being submitted and actually added to the endpoint queue (linked). If the device is disconnected during this time then usb core is not yet aware of the pending urb, and device may be freed just before xhci_urq_enqueue() continues, dereferencing the freed device. Freeing the device is protected by the xhci spinlock, so make sure we take and keep the lock while checking that device exists, dereference it, and add the urb to the queue. Remove the unnecessary URB check, usb core checks it before calling xhci_urb_enqueue() Suggested-by: Kuen-Han Tsai <khtsai@google.com> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20231201150647.1307406-20-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
e34900f46c
commit
e2e2aacf04
1 changed files with 23 additions and 17 deletions
|
@ -1521,24 +1521,7 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
|
|||
struct urb_priv *urb_priv;
|
||||
int num_tds;
|
||||
|
||||
if (!urb)
|
||||
return -EINVAL;
|
||||
ret = xhci_check_args(hcd, urb->dev, urb->ep,
|
||||
true, true, __func__);
|
||||
if (ret <= 0)
|
||||
return ret ? ret : -EINVAL;
|
||||
|
||||
slot_id = urb->dev->slot_id;
|
||||
ep_index = xhci_get_endpoint_index(&urb->ep->desc);
|
||||
ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state;
|
||||
|
||||
if (!HCD_HW_ACCESSIBLE(hcd))
|
||||
return -ESHUTDOWN;
|
||||
|
||||
if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) {
|
||||
xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(&urb->ep->desc))
|
||||
num_tds = urb->number_of_packets;
|
||||
|
@ -1562,12 +1545,35 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
|
|||
|
||||
spin_lock_irqsave(&xhci->lock, flags);
|
||||
|
||||
ret = xhci_check_args(hcd, urb->dev, urb->ep,
|
||||
true, true, __func__);
|
||||
if (ret <= 0) {
|
||||
ret = ret ? ret : -EINVAL;
|
||||
goto free_priv;
|
||||
}
|
||||
|
||||
slot_id = urb->dev->slot_id;
|
||||
|
||||
if (!HCD_HW_ACCESSIBLE(hcd)) {
|
||||
ret = -ESHUTDOWN;
|
||||
goto free_priv;
|
||||
}
|
||||
|
||||
if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) {
|
||||
xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n");
|
||||
ret = -ENODEV;
|
||||
goto free_priv;
|
||||
}
|
||||
|
||||
if (xhci->xhc_state & XHCI_STATE_DYING) {
|
||||
xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for non-responsive xHCI host.\n",
|
||||
urb->ep->desc.bEndpointAddress, urb);
|
||||
ret = -ESHUTDOWN;
|
||||
goto free_priv;
|
||||
}
|
||||
|
||||
ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state;
|
||||
|
||||
if (*ep_state & (EP_GETTING_STREAMS | EP_GETTING_NO_STREAMS)) {
|
||||
xhci_warn(xhci, "WARN: Can't enqueue URB, ep in streams transition state %x\n",
|
||||
*ep_state);
|
||||
|
|
Loading…
Reference in a new issue