# DF-5545 — xhci_pci_detach() swallows device_delete_children() failure and tears down under a live child

## What

`sys/bus/u4b/controller/xhci_pci.c:355` discards the return value of
`device_delete_children(self)` and unconditionally proceeds to halt the
controller, tear down the interrupt, release the IRQ + MMIO BAR, free all
USB DMA memory (`xhci_uninit` → `usb_bus_mem_free_all`, :378) and report
success (:380).

If any child device vetoes detach (`device_detach(child) != 0`,
sys/kern/subr_bus.c:1292), the parent still completes: newbus finishes the
"successful" detach, unbinds the driver and — via `device_set_driver(dev,
NULL)` in `device_detach` (sys/kern/subr_bus.c:2156) — **kfrees the
softc**. The vetoing child remains attached to a driverless parent whose
IRQ/MMIO/DMA are gone and whose softc is freed memory: a lying
`kldunload`/hot-unplug and a guaranteed use-after-free / NULL-deref for
whatever the child does next.

Exact family recurrence of DF-5544 (uhci_pci.c:374), DF-5528
(fwohci_pci.c) and DF-4558 (atapi-cam.c), inherited verbatim from FreeBSD.

## Reproduce (baseline, unpatched kernel)

Guest needs a real xHCI controller (NEC variant also serves DF-5546):
`QEMU_EXTRA="-device qemu-xhci" dfbsd-qemu/vm.sh up`

```
./build.sh          # builds veto.ko in the guest (KLD harness)
./run.sh            # kldload veto.ko; sysctl hw.df5545=1; sysctl hw.df5545=2
```

Expected output (see run.log / panic.txt / serial_console_full.log):
1. `veto0: DETACH VETO: returning EBUSY` — the child refuses
2. `xhci0: detached` + `device_detach(xhci0) returned 0` — **swallowed**
3. `xhci0 STILL has 1 child device(s): veto0(attached=1)` — orphan
4. `Fatal trap 12: page fault ... fault virtual address = 0x20` —
   `Stopped at df5545_thread+0x91: movq 0x20(%rax),%rdx` — **kernel
   panic** (NULL softc; on other access patterns, UAF read of the freed
   ring/DCBAA DMA pages via cmd 3)

## Fix validation

`fix.diff` (checked + propagated return). Applied in guest `/usr/src`
together with the DF-5546 fix, `make quickkernel && make installkernel`,
reboot, rerun the identical trigger (`run.fix.log`): `returned 16` —
**honest failure**, `xhci0: child device failed to detach; refusing
controller teardown`, driver still bound (state=3), ring/DCBAA buffers
untouched, no panic on the same cmd2 that killed the baseline; after the
child cooperates (`hw.df5545=5`) a retry completes a clean 0-children
detach. (The literal "SUCCESS ... (the lie)" label in the final retry is
a static harness string — the `STILL has 0 child device(s)` line right
after it is the actual state.)

## Honest scope

The vetoing child is a KLD stand-in for the newbus detach contract — no
in-tree u4b child detach returns nonzero today (`usb_detach` returns 0
unconditionally, sys/bus/u4b/controller/usb_controller.c:242), and a
DS_BUSY child propagates busy upward so `device_detach(xhci0)` itself
fails first. The hazard is the violated API contract: any current or
future child (or a grandchild detach veto resurfacing through
`device_delete_child`'s recursive retry, subr_bus.c:1292-1298) triggers
full teardown under a live child. Root-context trigger (kldunload /
PCI hot-unplug) ⇒ Low severity; mechanics certain and reproduced.
