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

## What

`sys/bus/u4b/controller/uhci_pci.c:374` discards the return value of
`device_delete_children(self)` and unconditionally proceeds to stop the
controller, tear down the interrupt, release IRQ + I/O, free all USB DMA
memory (`usb_bus_mem_free_all`, :409) and report success (:411).

If any child device vetoes detach (`device_detach(child) != 0`), the
parent still completes: newbus finishes the "successful" detach, unbinds
the driver and — via `device_set_driver(dev, NULL)` in
`sys/kern/subr_bus.c` — **kfrees the softc**. The vetoing child remains
attached to a driverless parent whose IRQ/IO/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.

## Reproduce (baseline, unpatched kernel)

Guest needs a real UHCI controller:
`QEMU_EXTRA="-device piix3-usb-uhci" dfbsd-qemu/vm.sh up`

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

Expected output (see run.log / panic.txt):
1. `veto0: DETACH VETO: returning EBUSY` — the child refuses
2. `uhci0: detached` + `device_detach(uhci0) returned 0` — **swallowed**
3. `uhci0 STILL has 1 child device(s): veto0(attached=1)` — orphan
4. `Fatal trap 12: page fault ... df5544_thread` — **kernel panic**

## Fix validation

`fix.diff` (checked + propagated return). Apply in guest `/usr/src`,
`make quickkernel && make installkernel`, reboot, rerun the same
trigger: `returned 16 -- honest failure`, controller/DMA intact, no
panic; retry after the child cooperates detaches cleanly (run.fix.log).

## 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(uhci0)` 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) triggers full teardown under a
live child. Same class as DF-5528 (fwohci_pci.c) and DF-4558
(atapi-cam.c), inherited verbatim from FreeBSD.
