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

**Finding.** `udav_attach()` (sys/bus/u4b/net/if_udav.c) has the DF-5334
"goto-detach-on-unattached" pattern: the `usbd_transfer_setup()` failure
branch (if_udav.c:265-268) and the `uether_ifattach()` failure branch
(if_udav.c:285-289) both jump to `detach:` (if_udav.c:293-295) and call the
full `udav_detach()`, which calls `uether_ifdetach(&sc->sc_ue)`
(if_udav.c:305) 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_udav.c:280-283 — *after* the failing call at if_udav.c:263-264:

- `usb_proc_drain(&ue->ue_tq)` (usb_ethernet.c:273) returns immediately
  (`up->up_lock == NULL`, usb_process.c:401-412 — its own comment says a
  zeroed struct makes it a no-op).
- `ifp = uether_getifp(ue)` (usb_ethernet.c:276) returns
  `&ue->ue_arpcom.ac_if` — an embedded member, never NULL — so the
  `if (ifp != NULL)` guard at :278 is dead code (same as DF-5334/5384).
- `UE_LOCK(ue)` (usb_ethernet.c:281, macro at :58) is
  `lockmgr(ue->ue_lock /* NULL */, LK_EXCLUSIVE)` → read at VA 0x0 →
  Fatal trap 12.

The second branch (uether_ifattach error) leaves `ue_lock` valid but the
ifnet never `if_attach()`ed; `uether_ifdetach()` proceeds to
`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 (sys/net/if.c) — latent list corruption
pre-empted by the NULL fault in branch 1. That branch needs
`usb_proc_create()` to fail (ENOMEM), so it is not attacker-timed; the
transfer-setup branch is fully device-controlled: any USB device exposing
one of the 6 `udav_devs[]` VID:PID pairs (if_udav.c:205-218; e.g. ShanTou
DM9601 0x0fe6:0x8101, Corega FEther USB-TXC 0x07aa:0x0004, Kontron JP1082)
at config 0 / iface 0 while omitting the bulk-IN/OUT endpoints makes
`usbd_transfer_setup()` fail (USB_ERR_NO_ENDPOINT) at plug-in.

**Verification** (evidence class: KLD harness driving production code —
QEMU has no DM9601 device model; `udav_attach`/`udav_detach` are static and
reachable only with hardware; aue DF-5384 / axge / mos precedents):

- The harness (`udav_attach_poc.c`) constructs the exact post-failure state
  (zeroed `struct udav_softc` + `lockinit(&sc->sc_lock)` per if_udav.c:260 —
  the only softc initialisation before the failing call) and replays
  `udav_detach()` verbatim: the production `usbd_transfer_unsetup()` then
  the production `uether_ifdetach()`.
- **Baseline** (pristine `with-src` guest, stock `/boot/kernel/uether.ko`
  auto-loaded by `MODULE_DEPEND`): the three markers print, then
  `Fatal trap 12: page fault while in kernel mode, fault virtual address
  = 0x0`, `current process = 1065 (kldload)`, `Stopped at
  lockmgr_exclusive+0x5d: orl (%r14),%esi` — guest dead at `db>`.
  Record: `panic.txt`. 1/1 run (deterministic NULL deref; DF-5334/5384
  proved the same sink in-situ).
- **Patched** (`fix.diff` applied to guest `/usr/src`; `uether.ko` +
  `if_udav.ko` rebuilt `-Werror` clean; patched `uether.ko` into
  `/boot/kernel`, md5-verified against the patched build): identical harness
  run prints `udav_poc: SURVIVED uether_ifdetach()` (the guard's early
  return), `KLDLOAD_RC=0`, `dmesg | grep -c 'Fatal trap'` = 0, `kldunload`
  clean, and the patched `if_udav.ko` driver module loads/unloads clean
  (2/2 runs). Records: `run.log`, `fix_uether_build.log`,
  `fix_udav_build.log`.

**Impact.** Plug-time kernel NULL dereference (panic/DoS) from a malicious
USB device — same severity tier and threat model as DF-5334/5384/5395/5397/
5399 (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/ifnet-list corruption) is
likewise plug-time corruption at fixed addresses — DoS.

**Why this is a separate finding from DF-5334/5384/5395/5397/5399.** Those
were filed against if_cdce.c / if_aue.c / if_axge.c / if_urndis.c /
if_mos.c; their persisted fixes were applied in-guest for validation only
and then 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_udav.c instance (root
cause identical, trigger device-controlled the same way).

**Fix.** `fix.diff`: (a) `uether_ifdetach()` early-returns when
`ue->ue_lock == NULL` (class-wide, matches the DF-5334/5384 validated fix);
(b) `udav_attach()` routes both pre-attach failures to a `detach_usb:`
label doing only `usbd_transfer_unsetup()` + `lockuninit()` and drops the
now-dead `detach:` label — (b) is required for the branch-2
`ether_ifdetach`-on-unattached corruption, which (a) does not cover.
Validated in-guest: baseline panics, patched survives, driver module clean.

**Pass-2 negative results (checked and killed).**
- RX pseudo-header/m_adj class (DF-5396 in urndis): udav BULK_DT_RD bufsize
  is `MCLBYTES+3` (if_udav.c:124); `actlen ≤ 2051`, guard
  `actlen < sizeof(stat)+ETHER_CRC_LEN` (:662) ensures payload ≥ 4;
  `len = min(actlen-3, le16toh(stat.pktlen)) - 4 ≤ MCLBYTES-4 = 2044`
  (:669-670). A device-supplied `pktlen < 4` makes `len` negative → passed
  to `uether_rxbuf(unsigned)` it wraps to ≥ 0xFFFFFFFC and is rejected by
  the usb_ethernet.c:589 guard (`len > MCLBYTES-ETHER_ALIGN`) — no OOB, the
  frame is just dropped. Copy lands at `m_adj(ETHER_ALIGN)` (=2) into a
  fresh 2048-byte cluster: 2+2044 = 2046 ≤ 2048. No DF-5396 instance.
- TX: `m->m_pkthdr.len` clamped to MCLBYTES (if_udav.c:594-595),
  `temp_len ≤ MCLBYTES+2 = bufsize` (:114, :602-611), copy at offset 2 →
  exact fit; `usbd_frame_zero` range within budget (:617-618).
- MII/register helpers: `reg & UDAV_EPAR_EROA_MASK` (5-bit) at :813-814/
  :853-854; multicast hash `h = crc32>>26 ≤ 63` into 8-byte table (:521-523).
  (The `udav_csr_read1()` uninitialized-return-on-error at :395-402 is filed
  separately as DF-5412, Info — it feeds device register writes only.)
- `udav_reset()` retry loop (:484-489): device can hold RST for
  1000 × hz/100 = 10 s under `sc_lock` — bounded plug-time stall, DoS
  ceiling too low to file (same code ships upstream).

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