# DF-5399 VERDICT — REPRODUCED (panic), fixed

**Finding.** `mos_attach()` (sys/bus/u4b/net/if_mos.c) has the
DF-5334 "goto-detach-on-unattached" pattern: the `usbd_transfer_setup()`
failure branch (if_mos.c:704-707) and the `uether_ifattach()` failure
branch (if_mos.c:722-726) both jump to `detach:` (if_mos.c:730-732) and
call the full `mos_detach()` (if_mos.c:756-767), which calls
`uether_ifdetach(&sc->sc_ue)` (if_mos.c:763) on a `usb_ether` that was
never attached.

**Why it faults.** On the transfer-setup failure path, every `ue` field
that `uether_ifdetach()` needs is still zero because they are assigned
only at if_mos.c:708-712 — *after* the failing call; before it only
`sc->mos_flags` (:694), `device_set_usb_desc()` (:696) and
`lockinit(&sc->sc_lock)` (:697) have run:

- `usb_proc_drain(&ue->ue_tq)` (usb_ethernet.c:273) returns immediately
  (`up->up_lock == NULL`, usb_process.c:406-410) — no fault, no
  initialization.
- `ifp = uether_getifp(ue)` (usb_ethernet.c:276) returns
  `&ue->ue_arpcom.ac_if` — never NULL, so the `if (ifp != NULL)` guard at
  :278 is dead code (same as DF-5334/DF-5384/DF-5395).
- `UE_LOCK(ue)` (usb_ethernet.c:281, macro at :61) is
  `lockmgr(ue->ue_lock /* NULL */, LK_EXCLUSIVE)` → read at VA 0x0 →
  Fatal trap 12.

The second branch (uether_ifattach error, only when `usb_proc_create()`
hits ENOMEM) leaves `ue_lock` valid but the ifnet never `if_attach()`ed;
`uether_ifdetach()` then proceeds to `usb_callout_drain()` on a
never-initialized watchdog and `ether_ifdetach()` (usb_ethernet.c:294) →
`if_detach()` on if_index 0 → nulls `ifindex2ifnet[0]` (lo0) and
`TAILQ_REMOVE`s a never-inserted ifnet from the global ifnet list —
latent list corruption, pre-empted in branch 1 by the NULL-write fault.
Branch 1 is the attacker-driven one: `mos_probe()` (if_mos.c:664-679)
matches on VID:PID + config/iface index only, never on descriptors, so
any evil USB adapter presenting MosChip 0x9710:0x7730 / 0x7830 / 0x7832
or Sitecom 0x0df6:0x0021 at config 0 / iface 0 while omitting the
bulk-IN/OUT endpoints makes `usbd_transfer_setup()` fail
(USB_ERR_NO_ENDPOINT) at plug-in → deterministic panic.

**Verification** (evidence class: KLD harness driving production code —
QEMU cannot emulate an MCS7730/7830/7830; `mos_attach`/`mos_detach` are
static and reachable only with hardware):

- The harness (`mos_attach_poc.c`) constructs the exact post-failure
  state (zeroed `struct mos_softc` + `lockinit(&sc->sc_lock)` per
  if_mos.c:697 — the only lock initialisation before the failing call at
  :700) and replays `mos_detach()` verbatim: the production
  `usbd_transfer_unsetup()` (if_mos.c:762) then the production
  `uether_ifdetach()` (if_mos.c:763). uether.ko auto-loads via
  MODULE_DEPEND.
- **Baseline** (pristine `with-src` guest, stock `/boot/kernel/uether.ko`):
  the three markers print (`ue_lock=0 ue_dev=0 ue_udev=0
  ue_tq.up_lock=0`), then `Fatal trap 12: page fault while in kernel
  mode, fault virtual address = 0x0`, `current process = 1008 (kldload)`,
  `Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi` — guest dead at
  DDB. Record: `panic.txt`. 1/1 run (deterministic NULL deref; the same
  sink was proved 3/3 in-situ by DF-5334 and reproduced for aue/axge).
- **Patched** (`fix.diff` applied to guest `/usr/src` via `patch -p1`
  Plan A, both hunks in both files; `uether.ko` + `if_mos.ko` rebuilt
  `-Werror` clean; patched `uether.ko` installed into `/boot/kernel`):
  the identical harness run prints `mos_poc: SURVIVED uether_ifdetach()`
  (the guard's early return), `KLDLOAD_RC=0`,
  `dmesg | grep -c 'Fatal trap'` = 0, `kldunload` clean (twice — stable),
  and the patched `if_mos.ko` driver module loads/unloads clean.
  Records: `run.log`, `fix_uether_build.log`, `fix_if_mos_build.log`.
- After validation the guest was `vm.sh reset with-src` — md5s of both
  touched sources match the pristine read-only tree, no artifacts left.

**Impact.** Plug-time kernel NULL dereference (panic/DoS) from a malicious
USB device — same severity tier and threat model as DF-5334/DF-5384/
DF-5395/DF-5397 (Medium, physical/VM USB access). NOT memory corruption
with attacker-controlled data: the fault address is fixed VA 0, the only
"dereference" is the lockmgr read of the lock word at NULL. No user→root
route. The latent branch-2 second stage (ifindex/ifnetlist corruption) is
likewise plug-time corruption of fixed addresses — DoS.

**Why this is a separate finding from DF-5334/DF-5384/DF-5395/DF-5397.**
Those were filed against if_cdce.c / if_aue.c / if_axge.c / if_urndis.c;
the persisted class-wide fix (uether_ifdetach guard) was applied in-guest
for validation only and reverted — the tree still carries the unguarded
`uether_ifdetach`, and every uether driver's attach-failure path is an
independent instance. This finding covers the if_mos.c instance (root
cause identical, trigger device-controlled the same way).

**Fix.** `fix.diff`: (a) `mos_attach()` routes both pre-attach failures to
a `detach_usb:` label doing only `usbd_transfer_unsetup()` +
`lockuninit()` — required for the branch-2 `ether_ifdetach`-on-unattached
corruption; (b) `uether_ifdetach()` early-returns when
`ue->ue_lock == NULL` (class-wide, matches the DF-5334/DF-5384/DF-5395
validated fix; protects every not-yet-converted uether driver).

## 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.
