cue_attach() failure paths call cue_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) β 8th instance of the DF-5334 uether goto-detach class
Summary
The device-controlled failure branch routes into the full detach while ue fields are assigned only AFTER the failing transfer_setup: an evil adapter with a cue_devs[] VID:PID (CATC Netmate/SmartBridge) 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 ether_ifdetaches a never-attached ifnet. if_cue.ko ships in the default module set and auto-loads. KLD-harness-verified: exact state replay panics at lockmgr_exclusive+0x5d; detach_usb rerouting + class-wide guard fix validated (SURVIVED, clean loads).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5486 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| cue_attach_poc.c | β | 4.5 KB | view raw | |
| Makefile | β | 147 B | β download | |
| build.log | β | 5.8 KB | view raw | |
| panic.txt | β | 1019 B | view raw | |
| run.log | β | 1004 B | view raw | |
| fix_uether_build.log | β | 7.0 KB | view raw | |
| fix_if_cue_build.log | β | 6.9 KB | view raw | |
| env.txt | β | 988 B | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| VERDICT.md | β | 6.8 KB | β raw | |
| README.md | β | 4.2 KB | β raw | |
| manifest.json | β | 1.2 KB | view raw | |
| verdict.json | β | 7.5 KB | view raw |
DF-5486 β if_cue.c attach-failure DF-5334-class panic (goto-detach-on-unattached)
What
cue_attach() (sys/bus/u4b/net/if_cue.c) routes its device-controlled
failure branch through cue_detach() on a usb_ether that was never
attached β identical class to DF-5334 (if_cdce.c), DF-5384 (if_aue.c),
DF-5395 (if_axge.c), DF-5397 (if_urndis.c), DF-5399 (if_mos.c),
DF-5411 (if_udav.c), DF-5453 (if_kue.c), all proved on this guest:
usbd_transfer_setup()fails (no bulk-in/bulk-out endpoints on an otherwise matching CATC device) βif_cue.c:407-410goto detachβif_cue.c:425-426cue_detach(dev).- At that point
ue->ue_lock,ue_dev,ue_udev,ue_methods,ue_tqare ALL still zero β they are assigned only atif_cue.c:412-416, after the failing call (lockinit(&sc->sc_lock,...)at :402 is the only softc initialization before it). cue_detach()(if_cue.c:437) callsuether_ifdetach(ue)(usb_ethernet.c:267-305):usb_proc_drain()is a no-op on the zeroedue_tq(usb_process.c:409);uether_getifp()returns the embedded arpcom ifnet (never NULL β theif (ifp != NULL)guard at :278 is dead code); thenusb_ethernet.c:281UE_LOCK(ue)=lockmgr(NULL, LK_EXCLUSIVE)β Fatal trap 12, fault VA 0x0.
The second failure branch (uether_ifattach() error, if_cue.c:418-422,
only reachable on usb_proc_create() ENOMEM) has the same shape and
additionally reaches ether_ifdetach() on a never-if_attach()ed ifnet β
ifindex2ifnet[0] nulling + global ifnet list corruption.
Threat model: attacker-supplied USB device (evil adapter / evil dock /
VM USB attach) presenting one of the 3 VID:PID pairs in cue_devs[]
(CATC NETMATE 0x042e:0x5600, NETMATE2 0x042e:0x5601, SmartBridges
SMARTLINK 0x087d:0x5600) at config 0 / iface 0 without the expected
bulk-in + bulk-out endpoints. Not remotely triggerable, not an
unprivileged-local program β physical/VM USB access. (Softc is zeroed by
newbus β subr_bus.c:1953 M_ZERO β so the fault address is fixed at 0,
not attacker-influenced.)
Reproduce (evidence class: KLD harness driving production code)
QEMU cannot emulate a CATC USB-EL1210A. The harness cue_attach_poc.c
replays cue_detach() verbatim (its two production calls, in order)
on the exact state cue_attach()'s failure branch produces, using the
production usbd_transfer_unsetup() and uether_ifdetach() (exported
by uether.ko). The only non-production code is the static
cue_attach/cue_detach frame itself (they are static in if_cue.c
and only reachable via goto detach with a physical CATC present).
# build (in guest, pack synced to /root/df5486)
cd /root/df5486 && make -m /usr/src/share/mk
# run (auto-loads STOCK /boot/kernel/uether.ko as dependency)
kldload -v ./cue_poc.ko
# expected: 3 marker lines, then
# Fatal trap 12: page fault while in kernel mode
# fault virtual address = 0x0
# Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi
# (guest dead in DDB)
Fix validation
fix.diff (git-apply-able against clean sys/, git apply --check OK):
(a) class-wide guard in uether_ifdetach() β early return when
ue->ue_lock == NULL; (b) cue_attach() routes both pre-attach failure
branches to a local detach_usb: label doing only
usbd_transfer_unsetup() + lockuninit() (fixes the branch-2
ether_ifdetach-on-unattached corruption too).
Applied to the guest's /usr/src (patch(1) Plan A, all hunks), both
modules rebuilt -Werror clean, patched uether.ko installed: the
identical harness run prints cue_poc: SURVIVED uether_ifdetach(),
KLDLOAD_RC=0, dmesg | grep -c 'Fatal trap' = 0, clean kldunload,
and the patched if_cue.ko driver loads/unloads clean.
See run.log vs panic.txt.
Files
cue_attach_poc.c+Makefileβ the harnessbuild.logβ harness build (full, -Werror)fix_uether_build.log/fix_if_cue_build.logβ patched module buildspanic.txtβ baseline serial record (stock modules)run.logβ patched run (survival proof) + dmesg markersenv.txtβ guest uname, kldstat, dmesg tail, compilerfix.diffβ the fix (never applied to the audit tree)VERDICT.md,manifest.json,verdict.json
VERDICT β DF-5486 (if_cue.c goto-detach-on-unattached panic)
status: reproduced / impact: panic / confidence: certain
Root cause (line-accurate)
- :402
lockinit(&sc->sc_lock, ...)β the ONLY softc initialization done before the failing call. - :405-410
usbd_transfer_setup(...); on error βgoto detach. - :412-416
ue->ue_sc/ue_dev/ue_udev/ue_lock/ue_methodsassigned β AFTER the failing call, so all still NULL/zero on the failure branch. - :418-422 second branch:
uether_ifattach(ue)error βgoto detach. - :425-427
detach: cue_detach(dev); return (ENXIO); - :436-437
cue_detach()runsusbd_transfer_unsetup()(safe on the zeroed xfer array) thenuether_ifdetach(ue).
sys/bus/u4b/net/usb_ethernet.c:
- :273
usb_proc_drain(&ue->ue_tq)β no-op on the zeroedue_tq(sys/bus/u4b/usb_process.c:409returns whenup->up_lock == NULL). - :276
ifp = uether_getifp(ue)=&ue->ue_arpcom.ac_if(:133-137) β embedded member, NEVER NULL. - :278
if (ifp != NULL)β dead guard, always true. - :281
UE_LOCK(ue)=lockmgr(ue->ue_lock=NULL, LK_EXCLUSIVE)(:61) β Fatal trap 12, fault VA 0x0.
Softc is zeroed by newbus (sys/kern/subr_bus.c:1953,
kmalloc(..., M_INTWAIT | M_ZERO)), so the fault address is fixed 0 β
a fixed-address NULL deref, not attacker-influenced memory corruption.
Branch 2 (uether_ifattach error β only usb_proc_create() ENOMEM,
usb_ethernet.c:173-178): ue_lock IS set, so uether_ifdetach
proceeds to ether_ifdetach() (:294) on an ifnet that was never
if_attach()ed (ue_attach_post_task never ran β it is queued only
AFTER successful usb_proc_create, :181-185) β sysctl_ctx_free() on a
never-initialized ctx (:297), devfs_clone_bitmap_put() of a unit never
allocated (:300), and ether_ifdetach tearing down a zeroed ifnet
(ifindex/ifnet-list corruption β same latent shape as DF-5334/5384).
Reachability (attacker model)
cue_probe() (:374-386) matches ANY of the 3 cue_devs[] VID:PIDs
(CATC NETMATE 0x042e:0x5600, NETMATE2 0x042e:0x5601, SmartBridges
SMARTLINK 0x087d:0x5600) at config index 0 / iface 0. cue_config
(:133-153) then requires UE_BULK endpoints in both directions
(UE_ADDR_ANY). A device presenting a matching VID:PID but with no bulk
endpoints (or descriptors that fail endpoint lookup) makes
usbd_transfer_setup() return an error β branch 1 β deterministic panic
at plug-in. Evil-USB-adapter / evil-dock / VM-attach scenario; not
remote, not unprivileged-local-software. if_cue.ko is in the default
module set (built by sys/bus/u4b/net/cue/Makefile, present as
/boot/kernel/if_cue.ko on the stock guest) and auto-loads on
device-match by uhub.
Evidence
panic.txt(baseline, stock modules): the 3cue_poc:markers, thenFatal trap 12: page fault while in kernel mode,fault virtual address = 0x0,current process = 1075 (kldload),Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi,db>β guest dead.- Harness (
cue_attach_poc.c) replayscue_detach()verbatim β its two production calls in order β on the exact branch-1 state (zeroedcue_softc,sc_locklockinit'd, everything else NULL), using the PRODUCTIONusbd_transfer_unsetup()+uether_ifdetach()from the stock auto-loadeduether.ko. QEMU cannot emulate a CATC USB-EL1210A, andcue_attach/cue_detachare static, so state+call-sequence replay is the strongest runnable evidence for this class (same evidence class as DF-5334/5384/5395/5397/5399/5411/5453). run.log(patched): identical harness under the fix β all 4 markers includingcue_poc: SURVIVED uether_ifdetach(),KLDLOAD_RC=0, cleankldunload,dmesg | grep -c 'Fatal trap'= 0 (checked twice), and the patchedif_cue.kodriver module loads/unloads clean.build.log,fix_uether_build.log,fix_if_cue_build.log:-Werrorclean builds of harness, patched uether.ko, patched if_cue.ko.
Why not higher severity
Fixed-address NULL deref (softc zeroed by newbus) at USB plug time; no attacker-controlled data reaches the fault. Impact = local DoS requiring physical/VM USB access β same tier as the 7 siblings (Medium, AV:P). No path to uid0 from this primitive.
Fix validated
fix.diff: (a) cue_attach() routes both pre-attach failures to a new
detach_usb: label doing only usbd_transfer_unsetup() +
lockuninit(); (b) class-wide belt-and-braces guard in
uether_ifdetach() (if (ue->ue_lock == NULL) return;). Applied
in-guest via patch -p1 (all hunks, Plan A), both modules rebuilt
-Werror clean, patched uether.ko installed to /boot/kernel, the
IDENTICAL harness re-run β SURVIVED, RC=0, 0 traps, clean unload;
patched driver module loads/unloads. git apply --check passes against
the clean read-only sys/ tree.
Pass-2 negative results (USB-net family checklist, all killed)
- (a) RX callback (
cue_bulk_read_callback, :443-490): gateactlen <= 2+sizeof(struct ether_header)at :459 drops runts; the device-supplied 16-bit frame length is clampedlen = min(actlen - 2, len)at :466-467;uether_rxbuf(usb_ethernet.c:589) independently rejectslen < ETHER_HDR_LEN || len > MCLBYTES - ETHER_ALIGNand copies into a fresh MCLBYTES cluster;2 + len <= actlen <= bufsize = MCLBYTES + 2(:149) β bounded, no OOB. Single frame per transfer (AOP_RXCOMBINE deliberately not set, :615-616) β no multi-frame m_adj accounting exists. - (b) TX (
cue_bulk_write_callback, :492-551):m_pkthdr.lenclamped to MCLBYTES (:513-514), frame_len = len+2 β€ MCLBYTES+2 = bufsize exactly (:139, :515);usbd_m_copy_inat offset 2 (:524) β exact fit. pkthdr.len desync from the mbuf chain (dead code at default if_mtu 1500) is benign forusbd_m_copy_inand BPF. No double-free; error path requeues via tr_setup. - (c) Register helpers (:194-271): all fixed-length β€8-byte stack
buffers;
cue_getmacfillsue_eaddr[ETHER_ADDR_LEN]exactly (usb_ethernet.h:107);cue_csr_read_2's uninitializedvalon request failure only feeds a collisions counter (:561-563) β no secret is returned to userspace.cue_initPAR0-i loop (:598-599) stays within 0x62-0x67, no register-index underflow. - (e) No m_adj/m_copyback/if_output in cue at all; the only device-fed lengths are the bounded 2-byte rx header and the 6-byte MAC.
- Known pass-1 findings NOT re-reported: DF-1081 / DF-2339
(
cue_setmulti8-bytehashtblfor the 64-byte 512-bit hash, :311/:329/:338 β 9-bit index OOB stack write).
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, all hunks); uether.ko and if_cue.ko rebuilt -Werror clean; patched uether.ko installed into /boot/kernel; the IDENTICAL harness re-run: 'cue_poc: SURVIVED uether_ifdetach()' in dmesg, KLDLOAD_RC=0, dmesg|grep -c 'Fatal trap' = 0, kldunload clean, and the patched if_cue.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 + kldstat + UNLOAD_OK + Fatal-trap count 0 + patched if_cue.ko load/unload); fix_uether_build.log / fix_if_cue_build.log (-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_cue.c:402
- sys/bus/u4b/net/if_cue.c:405-410
- sys/bus/u4b/net/if_cue.c:412-416
- sys/bus/u4b/net/if_cue.c:418-427
- sys/bus/u4b/net/if_cue.c:430-441
- sys/bus/u4b/net/if_cue.c:436-437
- 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/kern/subr_bus.c:1953
Detail
Evidence (decisive lines)
panic.txt (baseline serial: 3 cue_poc markers -> Fatal trap 12, fault virtual address = 0x0, current process 1075 (kldload), Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi, db> prompt); run.log (patched run: SURVIVED marker from dmesg, KLDLOAD_RC=0, kldstat, UNLOAD_OK, dmesg Fatal-trap count 0 checked twice, patched if_cue.ko loads clean); build.log + fix_uether_build.log + fix_if_cue_build.log (-Werror clean builds); env.txt (guest uname/kldstat/dmesg/compiler); fix.diff (git apply --check OK on clean read-only sys/ tree)
PoC changes
No seeded PoC existed. Wrote cue_attach_poc.c from scratch: KLD harness (MODULE_DEPEND uether) that kmallocs a zeroed struct cue_softc, lockinit()s sc->_lock exactly as if_cue.c:402 does (the only softc initialisation before the failing usbd_transfer_setup() at :405), prints the zeroed ue state, then replays cue_detach() verbatim: production usbd_transfer_unsetup(sc->sc_xfer, CUE_N_TRANSFER) (if_cue.c:436) + production uether_ifdetach(&sc->sc_ue) (if_cue.c:437). First build failed (-Werror) because the quoted source line 'return (0); / success /' inside the block comment closed it early and one UTF-8 em-dash hit -Werror on exposed code; fixed both, then -Werror clean. Physical CATC Netmate emulation is infeasible in QEMU (no usbredir gadget for this harness class); static cue_attach/cue_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 (same evidence class as the 7 sibling findings).
Verified recommended fix
cue_attach(): route both pre-attach failures to a detach_usb label doing only usbd_transfer_unsetup()+lockuninit(); uether_ifdetach(): early-return when ue->ue_lock==NULL (class-wide, same guard as DF-5334/5384 fix)
Verdict
Deterministic kernel panic at USB device plug-in: cue_attach()'s usbd_transfer_setup() failure branch (if_cue.c:407-410 'allocating USB transfers failed') goto-jumps into the full cue_detach() (if_cue.c:425-426) while the embedded usb_ether is still entirely zeroed (ue_lock/ue_dev/ue_udev/ue_methods/ue_tq are assigned only at if_cue.c:412-416, AFTER the failing call; lockinit of sc_lock at :402 is the only prior softc init). cue_detach() (if_cue.c:436-437) then calls uether_ifdetach(), which lockmgr()s through the NULL ue_lock (Fatal trap 12, fault VA 0x0, Stopped at lockmgr_exclusive+0x5d) - the DF-5334 (if_cdce) class, 8th driver instance: 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 3 cue_devs[] VID:PID pairs (CATC NETMATE 0x042e:0x5600, NETMATE2 0x042e:0x5601, SmartBridges SMARTLINK 0x087d:0x5600) at config 0/iface 0 without the expected bulk-in/bulk-out endpoints fails the transfer setup. The second 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. Verified with a KLD harness replaying cue_detach() verbatim (its two production calls in order) on the exact branch-1 state, using production usbd_transfer_unsetup()+uether_ifdetach(): baseline on with-src-reset 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) cue_attach detach_usb label doing only unsetup+lockuninit, required for branch 2) both modules rebuilt -Werror clean and the identical harness run SURVIVED with KLDLOAD_RC=0, 0 Fatal traps, clean unload, patched if_cue.ko driver loads/unloads clean (run.log). Softc is zeroed by newbus (subr_bus.c:1953 M_ZERO), so the fault address is fixed 0: not memory corruption with attacker-controlled data. Impact is panic/DoS from physical/VM USB access, same tier as the 7 siblings (Medium).
No comments yet.