kue_attach() failure paths call kue_detach() on a never-attached usb_ether -> deterministic kernel panic at evil-USB plug-in (lockmgr through NULL ue_lock; latent ether_ifdetach-on-unattached ifnet corruption on the third branch)
Summary
7th proven instance of the DF-5334 uether goto-detach class: all three attach failure branches route into the full detach while ue fields are assigned only AFTER the failing transfer_setup - the device-controlled branch (evil adapter with a kue_devs[] VID:PID omitting the bulk endpoints) runs uether_ifdetach on an entirely-zeroed usb_ether -> lockmgr(NULL) Fatal trap 12 VA 0; the uether_ifattach-error branch additionally ether_ifdetaches a never-attached ifnet and double-puts the unit. KLD-harness-verified: exact branch-1 state replay panics at lockmgr_exclusive+0x5d; detach_usb rerouting + class-wide ue_lock guard fix validated (SURVIVED, clean loads).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5453 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| kue_attach_poc.c | β | 4.2 KB | view raw | |
| Makefile | β | 147 B | β download | |
| build.sh | β | 99 B | view raw | |
| build.log | β | 5.8 KB | view raw | |
| panic.txt | β | 1020 B | view raw | |
| run.log | β | 1.1 KB | view raw | |
| fix_build.log | β | 1.7 KB | view raw | |
| env.txt | β | 211 B | view raw | |
| fix.diff | β | 1.8 KB | view raw | |
| VERDICT.md | β | 6.5 KB | β raw | |
| README.md | β | 2.6 KB | β raw | |
| manifest.json | β | 1.1 KB | view raw | |
| verdict.json | β | 6.8 KB | view raw |
DF-5453 β kue(4) attach-failure detach on never-attached usb_ether β kernel panic
File: sys/bus/u4b/net/if_kue.c (Kawasaki KL5KUSB101B USB ethernet, uether core)
Class: DF-5334 goto-detach family (7th sibling: cdce/aue/axge/urndis/mos/udav β kue)
Severity: Medium β evil-USB deterministic kernel panic (fixed-address NULL deref), physical/VM-USB access required.
Build (in guest)
cd /root/df5453 make -m /usr/src/share/mk
Run (baseline β stock kernel/modules)
kldload -v ./kue_poc.ko
Expected (baseline)
Three kue_poc: marker lines on the console, then
Fatal trap 12: page fault while in kernel mode fault virtual address = 0x0 current process = 1040 (kldload) Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi
guest dead in DDB (panic.txt).
Expected (with fix.diff modules)
kue_poc: SURVIVED uether_ifdetach() (UNEXPECTED β guard present?) KLDLOAD_RC=0
clean kldunload, dmesg | grep -c 'Fatal trap' = 0; the fixed if_kue.ko
driver module also loads/unloads cleanly (run.log).
What the harness does
kue_attach_poc.c is a KLD that reconstructs the exact post-usbd_transfer_setup()
-failure state of kue_attach() (if_kue.c:481-486): a zeroed struct kue_softc
whose only initialized field is sc_lock (if_kue.c:478) β ue_lock/ue_dev/ue_udev/
ue_methods/ue_tq are all still NULL because they are assigned only at
if_kue.c:495-499. It then replays kue_detach() verbatim, calling the
production usbd_transfer_unsetup() and uether_ifdetach() (exported by
uether.ko): uether_ifdetach() no-ops through usb_proc_drain() and hits
UE_LOCK(ue) = lockmgr(NULL, LK_EXCLUSIVE) β Fatal trap 12, VA 0x0.
The real-world trigger is fully device-controlled: an evil USB adapter
presenting one of the 33 kue_devs[] VID:PID pairs (e.g. KLSI DUH3E10BT
0x05e6:0x0008) at config 0 / iface 0 but without the expected bulk-in/out
endpoints makes usbd_transfer_setup() fail with USB_ERR_NO_PIPE
(usb_transfer.c:1030-1053) at plug-in time.
Fix validation
fix.diff (git-apply-able against clean sys/, verified with
git apply --check): (a) class-wide uether_ifdetach() early-return when
ue->ue_lock == NULL, (b) kue_attach() routes all three pre-attach failure
branches to a detach_usb label doing only usbd_transfer_unsetup() +
kfree(sc->sc_mcfilters) + lockuninit(). Applied in-guest to /usr/src,
uether.ko and if_kue.ko rebuilt -Werror clean, patched uether.ko
installed into /boot/kernel, guest rebooted, identical harness re-run β
SURVIVED, RC=0, 0 fatal traps.
DF-5453 VERDICT β reproduced (panic), fix validated (fixed)
Finding
kue_attach() (sys/bus/u4b/net/if_kue.c:469-511) routes all three of its
failure branches through goto detach; β kue_detach(dev); (if_kue.c:508-510),
but two of them run while the embedded usb_ether has not been configured
yet, and none of them correspond to a successfully attached interface:
- Branch 1 β
usbd_transfer_setup()failure (if_kue.c:481-486). Theue->ue_sc/ue_dev/ue_udev/ue_lock/ue_methodsassignments happen only at if_kue.c:495-499, i.e. after the failing call.ueis entirely zeroed (newbus softc isM_ZEROed) exceptsc_lock(lockinit'd at if_kue.c:478). - Branch 2 β
kmalloc(...M_WAITOK) == NULL(if_kue.c:488-493). Dead code on DragonFly (M_WAITOKcannot return NULL; withsc_descstill zeroed this is also akmalloc(0)β ZERO_LENGTH_PTR, which is DF-2340's root), but it would equally run with a zeroedue. - Branch 3 β
uether_ifattach()error (if_kue.c:501-505), reachable only viausb_proc_create()ENOMEM (usb_ethernet.c:173-178). Hereuefields are set but the embedded ifnet was neverif_attach()ed/ether_ifattach()ed andue_watchdogwas never callout-initialized (that happens inue_attach_post_task, usb_ethernet.c:208, which never ran).
Crash chain (branch 1, deterministic)
evil USB adapter (kue_devs[] VID:PID, config 0/iface 0, no bulk endpoints)
β usbd_transfer_setup() = USB_ERR_NO_PIPE (usb_transfer.c:1030-1053)
β kue_attach() goto detach β kue_detach() (if_kue.c:508-510)
β uether_ifdetach(ue) (if_kue.c:520)
β usb_proc_drain(&ue->ue_tq) (usb_ethernet.c:273) β no-op on
all-zero struct by design (usb_process.c:397-410)
β ifp = uether_getifp(ue) (usb_ethernet.c:276) β embedded
&ue->ue_arpcom.ac_if, NEVER NULL (usb_ethernet.c:133-137)
β if (ifp != NULL) (usb_ethernet.c:278) β dead guard
β UE_LOCK(ue) = lockmgr(NULL, LK_EXCLUSIVE) (usb_ethernet.c:61,281)
β Fatal trap 12, fault VA 0x0
Branch 3 additionally runs ether_ifdetach() on a never-attached ifnet and
devfs_clone_bitmap_put() of a never-allocated unit β the same latent
ifindex/ifnet-list corruption documented for the siblings (DF-5334/5384/5395/
5397/5399/5411); it needs kthread-create exhaustion so it is the latent, not
the demonstrated, half.
Reproduction
KLD harness kue_attach_poc.c (this pack) reconstructs the branch-1 state and
replays kue_detach()'s two production calls in order
(usbd_transfer_unsetup() + uether_ifdetach(), both from the stock
uether.ko). Baseline on the pristine with-src guest:
kue_poc: constructing post-'allocating USB transfers failed' state (if_kue.c:481-486) kue_poc: state check: ue_lock=0 ue_dev=0 ue_udev=0 ue_tq.up_lock=0 (all must be 0/NULL) kue_poc: entering kue_detach() replay: usbd_transfer_unsetup() + uether_ifdetach() (if_kue.c:519-520) Fatal user address access from kernel mode from kldload at ffffffff80647bad Fatal trap 12: page fault while in kernel mode fault virtual address = 0x0 current process = 1040 Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi
(panic.txt; guest dead in DDB, vm.sh status β down.)
Fix validation
fix.diff: (a) uether_ifdetach() early-returns when ue->ue_lock == NULL
(class-wide hardening, identical to the DF-5334/DF-5384 fix); (b) kue_attach()
routes all three failure gotos to a detach_usb label doing only
usbd_transfer_unsetup() + kfree(sc->sc_mcfilters) + lockuninit(&sc->sc_lock).
Applied in-guest to /usr/src (patch(1) Plan A, both files), uether.ko and
if_kue.ko rebuilt -Werror clean (fix_build.log), patched uether.ko
installed into /boot/kernel, guest rebooted, identical harness re-run:
kue_poc: SURVIVED uether_ifdetach() (UNEXPECTED β guard present?) KLDLOAD_RC=0 UNLOAD_OK FATAL_COUNT=0 KUE_DRIVER_LOAD_OK / KUE_DRIVER_UNLOAD_OK
(run.log). Baseline panic vs patched survival on identical input. fix.diff
verified git apply --check-able against the clean read-only sys/ tree.
Why not higher severity
Fixed-address NULL deref at device-attach time; no attacker-controlled data reaches memory corruption. Requires physical (or VM USB-passthrough) access with a crafted adapter β same tier as the six siblings (Medium). Not a userβroot primitive.
Pass-2 negative results (what else was hunted and killed)
- rx path (if_kue.c:531-578): frames shorter than 2+14 rejected
(if_kue.c:547); device-supplied 16-bit length prefix clamped by
len = min(actlen, len)(if_kue.c:555) withactlen β€ bufsize = MCLBYTES+2(xfer length set tousbd_xfer_max_len, if_kue.c:561);uether_rxbuf()then rejectslen < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGN(usb_ethernet.c:589) and copies at cluster offsetETHER_ALIGNβ exact fit, no OOB (unlike urndis DF-5396, kue is fully protected by the core check). Oversize/undersize frames are silently dropped (return value ofuether_rxbufignored β accounting nit only). - tx path (if_kue.c:580-646):
m->m_pkthdr.lenclamped to MCLBYTES (if_kue.c:603-604) β only ever lowered, sousbd_m_copy_instays in bounds;temp_len β€ MCLBYTES+2,total_len = temp_len + (64 - temp_len%64) β€ MCLBYTES+66 β β€ MCLBYTES+2+64 = bufsize(if_kue.c:180,606,618); padding viausbd_frame_zeroin-bounds. BPF after copy. Bounded. - mii/word helpers: kue has no MII methods (if_kue.c:220-227);
kue_setword/kue_ctlbuild fixed stack requests,wLengthonly from compile-timesizeofs (if_kue.c:247-279). - device-supplied data: the 2-byte rx prefix (dead end, above);
sc_descfeeds onlyue_eaddrandKUE_MCFILTCNTβ the latter is the known DF-1075/DF-2340 issue (not re-reported). Ignoredkue_ctl()return inkue_attach_post(if_kue.c:439-443) yields an all-zero MAC on control transfer failure β robustness nit, no memory-safety impact. - lifetimes/locks: rx/tx callbacks run under
sc->sc_xferlock =sc->sc_lock;uether_ifdetachdrains the taskqueue before ifnet teardown; watchdog only armed whenue_tickset (kue sets none).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedfix.diff applied in-guest to /usr/src (patch(1) Plan A, both files); uether.ko and if_kue.ko rebuilt -Werror clean (fix_build.log); patched uether.ko installed into /boot/kernel; guest rebooted and the IDENTICAL harness re-run: 'kue_poc: SURVIVED uether_ifdetach()', KLDLOAD_RC=0, dmesg|grep -c 'Fatal trap' = 0, kldunload clean, and the patched if_kue.ko driver module loads/unloads clean. Baseline panic record in panic.txt, patched survival in run.log. fix.diff verified git-apply-able against the clean read-only sys/ tree (git apply --check OK).
run.log (SURVIVED marker + KLDLOAD_RC=0 + UNLOAD_OK + FATAL_COUNT=0 + KUE_DRIVER_LOAD_OK); fix_build.log (uether.ko + if_kue.ko -Werror clean); panic.txt (baseline trap on identical input); fix.diff (git apply --check OK on clean tree)
Confirmed kernel references
- sys/bus/u4b/net/if_kue.c:481-486
- sys/bus/u4b/net/if_kue.c:488-493
- sys/bus/u4b/net/if_kue.c:495-499
- sys/bus/u4b/net/if_kue.c:501-510
- sys/bus/u4b/net/if_kue.c:513-525
- sys/bus/u4b/net/usb_ethernet.c:61
- sys/bus/u4b/net/usb_ethernet.c:133-137
- sys/bus/u4b/net/usb_ethernet.c:267-305
- sys/bus/u4b/usb_process.c:397-410
- sys/bus/u4b/usb_transfer.c:1030-1053
Detail
Evidence (decisive lines)
panic.txt (baseline serial: 3 kue_poc markers -> Fatal trap 12, fault virtual address = 0x0, current process 1040 (kldload), Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi, db> prompt); run.log (patched run: SURVIVED marker, KLDLOAD_RC=0, UNLOAD_OK, FATAL_COUNT=0, KUE_DRIVER_LOAD_OK/UNLOAD_OK); build.log + fix_build.log (uether.ko + if_kue.ko -Werror clean); env.txt (guest uname/dmesg/kldstat); fix.diff (git-apply-able against clean sys/, git apply --check OK)
PoC changes
No seeded PoC existed. Wrote kue_attach_poc.c from scratch (mirroring the DF-5384 aue harness of the same class): KLD harness (MODULE_DEPEND uether) that kmallocs a zeroed struct kue_softc, lockinit()s sc->sc_lock exactly as if_kue.c:478 does (the only softc initialisation before the failing usbd_transfer_setup() at :481), prints the zeroed ue state, then replays kue_detach() verbatim: production usbd_transfer_unsetup(sc->sc_xfer, KUE_N_TRANSFER) (if_kue.c:519) + production uether_ifdetach(&sc->sc_ue) (if_kue.c:520). Physical KL5KUSB101B emulation is infeasible in QEMU (no usbredir gadget for this harness class); static kue_attach/kue_detach cannot be linked against, so the branch-1 state + exact call-sequence replay is the strongest runnable evidence and uses only production code after the goto.
Verified recommended fix
kue_attach(): route all three pre-attach failures to a detach_usb label doing only usbd_transfer_unsetup()+kfree(sc_mcfilters)+lockuninit(); uether_ifdetach(): early-return when ue->ue_lock==NULL (class-wide, same as DF-5334/DF-5384 fix)
Verdict
Deterministic kernel panic at USB device plug-in: kue_attach()'s usbd_transfer_setup() failure branch (if_kue.c:483-486 'allocating USB transfers failed') goto-jumps into the full kue_detach() (if_kue.c:508-510) while the embedded usb_ether is still entirely zeroed (ue_lock/ue_dev/ue_udev/ue_methods/ue_tq are assigned only at if_kue.c:495-499, AFTER the failing call). uether_ifdetach() then lockmgr()s through the NULL ue_lock (Fatal trap 12, fault VA 0x0, Stopped at lockmgr_exclusive+0x5d) - the 7th instance of the DF-5334 (if_cdce) class proven this pass after cdce/aue/axge/urndis/mos/udav: usb_proc_drain() no-ops on the zeroed ue_tq and the ifp!=NULL guard is dead code because uether_getifp() returns the embedded arpcom ifnet. The trigger is fully device-controlled: any evil USB adapter presenting one of the 33 kue_devs[] VID:PID pairs (e.g. KLSI DUH3E10BT 0x05e6:0x0008 or 3Com 3C460 0x04cf:0x7992) at config 0/iface 0 without the expected bulk-in/out endpoints fails the transfer setup with USB_ERR_NO_PIPE (usb_transfer.c:1030-1053). The third failure branch (uether_ifattach error, only on usb_proc_create ENOMEM) additionally runs ether_ifdetach() on a never-if_attach()ed ifnet -> ifindex2ifnet[0] nulling + global ifnet list corruption (latent, same as siblings); the second branch (kmalloc M_WAITOK NULL check) is dead code on DragonFly. Verified with a KLD harness replaying kue_detach() verbatim (its two production calls in order) on the exact branch-1 state, using production usbd_transfer_unsetup()+uether_ifdetach(): baseline on pristine with-src guest with stock uether.ko -> Fatal trap 12 VA 0x0, guest dead in DDB (panic.txt); with fix.diff ((a) uether_ifdetach early-return on NULL ue_lock + (b) kue_attach detach_usb label doing only unsetup+kfree+lockuninit) both modules rebuilt -Werror clean and the identical harness run SURVIVED with KLDLOAD_RC=0, 0 Fatal traps, clean unload, and the patched if_kue.ko driver loads/unloads clean (run.log). Not memory corruption with attacker-controlled data: fixed-address NULL deref at plug time; impact is panic/DoS from physical/VM USB access, same tier as the six siblings (Medium).
No comments yet.