Commit 203a8661 authored by Sarah Sharp's avatar Sarah Sharp

xhci: Avoid NULL pointer deref when host dies.

When the host controller fails to respond to an Enable Slot command, and
the host fails to respond to the register write to abort the command
ring, the xHCI driver will assume the host is dead, and call
usb_hc_died().

The USB device's slot_id is still set to zero, and the pointer stored at
xhci->devs[0] will always be NULL.  The call to xhci_check_args in
xhci_free_dev should have caught the NULL virt_dev pointer.

However, xhci_free_dev is designed to free the xhci_virt_device
structures, even if the host is dead, so that we don't leak kernel
memory.  xhci_free_dev checks the return value from the generic
xhci_check_args function.  If the return value is -ENODEV, it carries on
trying to free the virtual device.

The issue is that xhci_check_args looks at the host controller state
before it looks at the xhci_virt_device pointer.  It will return -ENIVAL
because the host is dead, and xhci_free_dev will ignore the return
value, and happily dereference the NULL xhci_virt_device pointer.

The fix is to make sure that xhci_check_args checks the xhci_virt_device
pointer before it checks the host state.

See https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1203453 for
further details.  This patch doesn't solve the underlying issue, but
will ensure we don't see any more NULL pointer dereferences because of
the issue.

This patch should be backported to kernels as old as 3.1, that
contain the commit 7bd89b40 "xhci: Don't
submit commands or URBs to halted hosts."
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Reported-by: default avatarVincent Thiele <vincentthiele@gmail.com>
Cc: stable@vger.kernel.org
parent 94190301
...@@ -1181,9 +1181,6 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, ...@@ -1181,9 +1181,6 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
} }
xhci = hcd_to_xhci(hcd); xhci = hcd_to_xhci(hcd);
if (xhci->xhc_state & XHCI_STATE_HALTED)
return -ENODEV;
if (check_virt_dev) { if (check_virt_dev) {
if (!udev->slot_id || !xhci->devs[udev->slot_id]) { if (!udev->slot_id || !xhci->devs[udev->slot_id]) {
printk(KERN_DEBUG "xHCI %s called with unaddressed " printk(KERN_DEBUG "xHCI %s called with unaddressed "
...@@ -1199,6 +1196,9 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, ...@@ -1199,6 +1196,9 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev,
} }
} }
if (xhci->xhc_state & XHCI_STATE_HALTED)
return -ENODEV;
return 1; return 1;
} }
......
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