linux/drivers/usb/dwc2
Doug Anderson dc87308469 usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.

It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD.  If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case.  This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).

The specific crash I found was:
 Unable to handle kernel paging request at virtual address 6b6b6b9f

At the time of the crash, the kernel reported:
 (dwc2_hc_nak_intr+0x5c/0x198)
 (dwc2_handle_hcd_intr+0xa84/0xbf8)
 (_dwc2_hcd_irq+0x1c/0x20)
 (usb_hcd_irq+0x34/0x48)

Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.

kgdb gave a little better stack crawl:
 0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
     chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
     qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
 1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
     drivers/usb/dwc2/hcd_intr.c:2041
 2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
 3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
     drivers/usb/dwc2/hcd_intr.c:2128
 4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
 5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
     drivers/usb/core/hcd.c:2353

Popping up to frame #1 (dwc2_hc_n_intr) found:
 (gdb) print /x hcint
 $12 = 0x12

AKA:
 #define HCINTMSK_CHHLTD  (1 << 1)
 #define HCINTMSK_NAK     (1 << 4)

Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free.  See <http://crosreview.com/305712> for a patch and
instructions.  This lead to getting the following stack crawl of the
actual free:
 0  arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
 1  kgdb_breakpoint () at kernel/debug/debug_core.c:1054
 2  dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
      out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
 3  dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
      hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
 4  dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
      chan=chan@entry=0xed5be000, qtd=<optimized out>,
      halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
 5  dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
      out>, halt_status=<optimized out>) at
      drivers/usb/dwc2/hcd_intr.c:804
 6  dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
      halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
      out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
 7  dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
      chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
      qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
 8  dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
      hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
 9  dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
      hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
 10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
      drivers/usb/dwc2/hcd_intr.c:2052
 11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
 12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
      drivers/usb/dwc2/hcd_intr.c:2147
 13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
 14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
      drivers/usb/core/hcd.c:2353

Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.

Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-19 09:22:46 -05:00
..
core_intr.c usb: dwc2: exit hibernation on session request 2015-10-01 12:40:27 -05:00
core.c usb: dwc2: fix duplicate argument warning 2015-10-13 13:17:42 -05:00
core.h usb: dwc2: refactor common low-level hw code to platform.c 2015-10-14 08:40:15 -05:00
debug.h usb: dwc2: move debugfs code to a separate file 2015-04-29 15:18:25 -05:00
debugfs.c usb: dwc2: Use platform endianness when accessing registers 2015-09-27 10:54:31 -05:00
gadget.c usb: dwc2: refactor common low-level hw code to platform.c 2015-10-14 08:40:15 -05:00
hcd_ddma.c usb: dwc2: Use platform endianness when accessing registers 2015-09-27 10:54:31 -05:00
hcd_intr.c usb: dwc2: host: Fix use after free w/ simultaneous irqs 2015-10-19 09:22:46 -05:00
hcd_queue.c usb: dwc2: host: use correct frame number during qh init 2015-10-01 12:40:18 -05:00
hcd.c usb: dwc2: host: Protect PCGCTL with lock in dwc2_port_resume() 2015-10-16 14:44:15 -05:00
hcd.h usb: dwc2: Use platform endianness when accessing registers 2015-09-27 10:54:31 -05:00
hw.h usb: dwc2: gadget: parity fix in isochronous mode 2015-10-02 13:10:57 -05:00
Kconfig usb: dwc2: remove dwc2_platform.ko 2015-04-29 15:20:11 -05:00
Makefile usb: dwc2: remove dwc2_platform.ko 2015-04-29 15:20:11 -05:00
pci.c usb: dwc2: pci: Add device mode to the dwc2-pci driver 2015-03-11 15:08:17 -05:00
platform.c usb: dwc2: refactor common low-level hw code to platform.c 2015-10-14 08:40:15 -05:00