# VERDICT — DF-5514 (if_ipheth.c goto-detach-on-unattached, 9th DF-5334 instance)

**Status: reproduced (panic, deterministic) · fix validated (fixed)**

## What was claimed and what the code says

`ipheth_attach()` (sys/bus/u4b/net/if_ipheth.c:217-265) has four error branches that all
`goto detach;` (:235, :241, :252, :258), and the `detach:` label (:262-263) calls the **full**
`ipheth_detach(dev)` — which calls `uether_ifdetach(&sc->sc_ue)` (:276) — even when
`uether_ifattach(ue)` (:255) was never reached or never succeeded.

* **Branches 1-2** (`usbd_set_alt_interface_index` :231-236 fails; `usbd_transfer_setup`
  :237-242 fails): the `ue->ue_sc/ue_dev/ue_udev/ue_lock/ue_methods` assignments at :243-247
  run only *after* both calls, so the embedded `usb_ether` is still **entirely zeroed**
  (newbus allocates the softc `M_ZERO`, sys/kern/subr_bus.c:1953).
* `uether_ifdetach()` (sys/bus/u4b/net/usb_ethernet.c:267-305) then:
  * `usb_proc_drain(&ue->ue_tq)` (:273) — no-op, `up->up_lock == NULL` early-returns
    (sys/bus/u4b/usb_process.c:397-410);
  * `ifp = uether_getifp(ue)` (:276) — returns `&ue->ue_arpcom.ac_if`
    (usb_ethernet.c:133-137), **never NULL** because the arpcom is embedded in the softc
    (usb_ethernet.h:80-84) — the `if (ifp != NULL)` guard at :278 is dead code;
  * `UE_LOCK(ue)` (:281) → `lockmgr(NULL, LK_EXCLUSIVE)` → **Fatal trap 12, fault VA 0x0**.
* **Branch 3** (`ipheth_get_mac_addr` :249-253 fails — device just STALLs the `0x00`
  GET_MACADDR vendor request): `ue_lock` is valid, but `uether_ifattach()` never ran, so
  `ue_tq` is still zeroed (drain no-op) and the code proceeds to `ether_ifdetach(ifp)`
  (:294) on the embedded ifnet that was **never `if_attach()`ed** — the DF-2341/DF-5493
  latent corruption/panic path (`ifindex2ifnet[0]`/ifnet-list damage).
* **Branch 4** (`uether_ifattach` :255-259 fails): the only failure mode is
  `usb_proc_create()` ENOMEM, whose error path **self-frees** the `ue_tq`
  (usb_process.c `usb_proc_create` → `usb_proc_free`), so the state is branch-3-like:
  valid `ue_lock`, zeroed tq, never-attached ifnet.

The trigger is fully device-controlled: `ipheth_devs[]` is a **product-agnostic** match
(if_ipheth.c:174-177) — any USB device with vendor 0x05ac exposing interface class
0xff / subclass 0xfd / protocol 1. Branch 1 additionally needs only a STALLed
`SET_INTERFACE(alt=1)` — trivial for evil-adapter/dock/VM firmware, no iPhone needed.

## How it was verified (KLD-harness-with-production-code)

No QEMU iPhone model exists, and `ipheth_attach`/`ipheth_detach` are static (unreachable
without a physical Apple device), so — exactly like the 8 sibling packs — the harness
(`ipheth_poc.c`) reconstructs the **branch-1 post-failure state** (`kmalloc(M_ZERO)` softc,
`sc_iface_no = 0` per :225, `lockinit(&sc->sc_lock, ...)` per :229 — the only softc init
before the failing call) and replays `ipheth_detach()` **verbatim** using only production
code after the `goto`: `usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER)` (:274) and
`uether_ifdetach(&sc->sc_ue)` (:276), linked from stock `uether.ko`.

### Baseline (stock uether.ko, fresh guest)

`kldload -v /root/df5514/ipheth_poc.ko` → guest died in DDB, serial console
(`panic.txt`):

```
ipheth_poc: state check: ue_lock=0 ue_dev=0 ue_udev=0 ue_tq.up_lock=0 (all must be 0/NULL)
ipheth_poc: entering ipheth_detach() replay: usbd_transfer_unsetup() + uether_ifdetach() (if_ipheth.c:274+276)
Fatal user address access from kernel mode from kldload at ffffffff80647bad
Fatal trap 12: page fault while in kernel mode
fault virtual address        = 0x0
Stopped at      lockmgr_exclusive+0x5d: orl     (%r14),%esi
```

Identical trap signature to all 8 siblings (`lockmgr_exclusive+0x5d`, VA 0x0).
`vm.sh status` → down. The fault address is fixed 0 (NULL deref) — panic/DoS, not
attacker-controlled-data corruption; no privilege-escalation path exists from this primitive
(nothing is written before the fault), consistent with the class's Medium rating.

### Fix validation (`fix.diff`)

1. `vm.sh reset with-src` (clean source + clean modules), apply `fix.diff` in-guest to
   `/usr/src` (both hunks, Plan A).
2. Rebuild `uether.ko` and `if_ipheth.ko` — both `-Werror` clean (`fix_build.log`).
3. Install patched `uether.ko` into `/boot/kernel`.
4. Re-run the **byte-identical** harness (md5 `f27308481209578091ab79c853df1979`):
   `ipheth_poc: SURVIVED uether_ifdetach()`, `KLDLOAD_RC=0`, `kldunload` clean,
   `dmesg | grep -c "Fatal trap"` = **0** (`run.log`).
5. Patched driver module `if_ipheth.ko` also loads/unloads clean (`fix_smoke.log`).

`fix.diff` = (a) class-wide core guard: `uether_ifdetach()` early-returns when
`ue->ue_lock == NULL` (fixes branches 1-2 everywhere); (b) driver: all four pre-attach
error branches route to a minimal `detach_usb` label doing only
`usbd_transfer_unsetup()` + `lockuninit()` (required for branches 3-4, where `ue_lock` is
already valid but the ifnet was never attached). `git apply --check` passes against the
clean read-only `sys/` tree.

## Pass-2 negative space (everything else on the USB-net checklist, killed with proof)

* **(a) RX callback / m_adj accounting (DF-5396 class) — BOUNDED.**
  `uether_newbuf()` returns an MCLBYTES cluster mbuf and *itself* applies
  `m_adj(ETHER_ALIGN)` (usb_ethernet.c:549-560: m_data=cl+2, m_len=2046); ipheth adds a
  second `m_adj(m, ETHER_ALIGN)` at :511 (m_data=cl+4, m_len=2044 — matches FreeBSD
  verbatim, wastes 2 bytes, harmless). `usbd_xfer_set_frame_data(xfer, 0, m->m_data,
  m->m_len)` (:518) programs the HC DMA to ≤2044 bytes into [cl+4, cl+2048). At
  TRANSFERRED, `len = usbd_xfer_frame_len()` (:487) is the **host-controller-clamped
  actual length** ≤2044 — NOT a device-parsed header field like urndis's `rm_datalen`.
  Gate `len < 14+2` frees (:489-493); `m_adj(m, 2)` (:495) → m_len=2042;
  `uether_rxmbuf(ue, m, len-2)` (:498) sets m_len=len-2 ≤ 2042 = remaining cluster
  capacity exactly — **no OOB possible**. `aframes` ≤ requested frames = 1
  (`IPHETH_RX_FRAMES_MAX=1`), so no multi-frame accounting exists
  (KASSERT frindex < max_frame_count, usb_transfer.c:2148-2153). No mos-DF-5400-style
  actlen-vs-pktlen confusion: per-frame length is used, not whole-transfer actlen.
* **(b) TX path — BOUNDED.** `m->m_pkthdr.len` clamped to `IPHETH_BUF_SIZE`=1516 (:422-423);
  `usbd_m_copy_in(pc, 0, m, 0, ≤1516)` (:425) into frame slot x of capacity 1516 at offset
  `x*1516` ≤ 10612 < bufsize 8*1516=12128 (:415-416); `usbd_frame_zero(pc, len, 1516-len)`
  stays in-slot (:430-431); x ≤ 8. Error path frees `sc_tx_buf` before `tr_setup`
  re-entry (:452, :457-461); CANCELLED path frees (:452, :539). No double-free, no leak.
* **(c) register/carrier helpers — exact fit.** `ipheth_get_mac_addr`: wLength=6 into
  `sc_data[64]` + 6-byte memcpy to `ue_eaddr[6]` (:187-201). `ipheth_tick`: wLength=64 ==
  `sizeof(sc_data)` (:320, var.h:53,75). Note: `sc_carrier_on` (:328-329) is **never read
  anywhere** — carrier check is dead code, and a short control response would leave stale
  `sc_data[0]`; since the value is never consumed, no impact (Info, not filed).
* **(d) attach/detach error paths — THE finding (above).** Also checked: `lockuninit` after
  the panic point only; `usb_proc_create` failure self-frees (label-safe for branch 4);
  branch-3/4 `ether_ifdetach`-on-unattached = DF-2341/DF-5493 mechanism (core-owned).
* **(e) device-supplied data feeding m_adj/m_copyback/if_output — none beyond the above.**
  Device-fed inputs are only: the 6-byte MAC (spoofable like any USB NIC — functional),
  the carrier byte (dead), and the HC-bounded RX length. No RNDIS-style parsed headers
  exist in this driver. VLAN frames >1516 are silently truncated on TX (:422-423) —
  inherited verbatim from Linux/FreeBSD ipheth semantics (device protocol limit 1516),
  functional only, not filed.

## Bottom line

`ipheth_attach()`'s four `goto detach` branches run the full `ipheth_detach()` →
`uether_ifdetach()` on a `usb_ether` that was never attached: branches 1-2 fault at
`lockmgr(NULL)` (Fatal trap 12, VA 0x0 — reproduced deterministically on the guest with a
production-code KLD harness), branches 3-4 reach `ether_ifdetach()` on a never-attached
ifnet. Fully triggerable by an evil USB adapter (any Apple VID + class ff/fd/1 interface)
at plug-in. Fixed by the class-wide core guard + minimal `detach_usb` label; identical
harness survives on the patched build. DoS/panic tier, matching the 8 siblings (Medium).

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