# VERDICT — DF-5486 (if_cue.c goto-detach-on-unattached panic)

**status: reproduced / impact: panic / confidence: certain**

## Root cause (line-accurate)

`sys/bus/u4b/net/if_cue.c`:

- :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_methods` assigned —
  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()` runs `usbd_transfer_unsetup()` (safe on the
  zeroed xfer array) then `uether_ifdetach(ue)`.

`sys/bus/u4b/net/usb_ethernet.c`:

- :273 `usb_proc_drain(&ue->ue_tq)` — no-op on the zeroed `ue_tq`
  (`sys/bus/u4b/usb_process.c:409` returns when `up->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 3 `cue_poc:` markers, then
  `Fatal 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`) replays `cue_detach()` verbatim — its two
  production calls in order — on the exact branch-1 state (zeroed
  `cue_softc`, `sc_lock` lockinit'd, everything else NULL), using the
  PRODUCTION `usbd_transfer_unsetup()` + `uether_ifdetach()` from the
  stock auto-loaded `uether.ko`. QEMU cannot emulate a CATC USB-EL1210A,
  and `cue_attach`/`cue_detach` are 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
  including `cue_poc: SURVIVED uether_ifdetach()`, `KLDLOAD_RC=0`,
  clean `kldunload`, `dmesg | grep -c 'Fatal trap'` = 0 (checked twice),
  and the patched `if_cue.ko` driver module loads/unloads clean.
- `build.log`, `fix_uether_build.log`, `fix_if_cue_build.log`:
  `-Werror` clean 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): gate
  `actlen <= 2+sizeof(struct ether_header)` at :459 drops runts;
  the device-supplied 16-bit frame length is clamped
  `len = min(actlen - 2, len)` at :466-467; `uether_rxbuf`
  (usb_ethernet.c:589) independently rejects `len < ETHER_HDR_LEN ||
  len > MCLBYTES - ETHER_ALIGN` and 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.len` clamped
  to MCLBYTES (:513-514), frame_len = len+2 ≤ MCLBYTES+2 = bufsize
  exactly (:139, :515); `usbd_m_copy_in` at offset 2 (:524) → exact
  fit. pkthdr.len desync from the mbuf chain (dead code at default
  if_mtu 1500) is benign for `usbd_m_copy_in` and BPF. No double-free;
  error path requeues via tr_setup.
- (c) Register helpers (:194-271): all fixed-length ≤8-byte stack
  buffers; `cue_getmac` fills `ue_eaddr[ETHER_ADDR_LEN]` exactly
  (usb_ethernet.h:107); `cue_csr_read_2`'s uninitialized `val` on
  request failure only feeds a collisions *counter* (:561-563) — no
  secret is returned to userspace. `cue_init` PAR0-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_setmulti` 8-byte `hashtbl` for 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.
