ipheth_attach() failure paths call ipheth_detach() on a never-attached usb_ether -> deterministic kernel panic at evil-USB plug-in (lockmgr through NULL ue_lock on branches 1-2; ether_ifdetach on never-if_attach()ed embedded ifnet on branches 3-4) β 9th and final instance of the DF-5334 uether goto-detach class
Summary
All four error branches route into the full detach while the ue fields are assigned only after the failing calls: branches 1-2 (STALL SET_INTERFACE / missing bulk endpoints) run uether_ifdetach with ue_lock NULL -> lockmgr(NULL) Fatal trap 12; branches 3-4 (GET_MACADDR STALL / uether_ifattach ENOMEM) ether_ifdetach a never-attached ifnet. The probe is PRODUCT-AGNOSTIC (any 0x05ac vendor device with class 0xff/0xfd/1) and the firmware only needs to STALL one request. KLD-harness-verified: exact pre-failure state replay panics at lockmgr_exclusive+0x5d; detach_usb rerouting + core guard fix validated (SURVIVED, clean loads). 9th and final instance; owner DF-5493.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5514 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| ipheth_poc.c | β | 5.9 KB | view raw | |
| Makefile | β | 146 B | β download | |
| build.sh | β | 152 B | view raw | |
| run.sh | β | 227 B | view raw | |
| build.log | β | 6.0 KB | view raw | |
| panic.txt | β | 2.2 KB | view raw | |
| run.log | β | 606 B | view raw | |
| fix_build.log | β | 14.1 KB | view raw | |
| fix_smoke.log | β | 83 B | view raw | |
| env.txt | β | 991 B | view raw | |
| fix.diff | β | 2.0 KB | view raw | |
| VERDICT.md | β | 8.7 KB | β raw | |
| manifest.json | β | 2.0 KB | view raw | |
| verdict.json | β | 8.6 KB | view raw |
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): theue->ue_sc/ue_dev/ue_udev/ue_lock/ue_methodsassignments at :243-247 run only after both calls, so the embeddedusb_etheris still entirely zeroed (newbus allocates the softcM_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 == NULLearly-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) β theif (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 the0x00GET_MACADDR vendor request):ue_lockis valid, butuether_ifattach()never ran, soue_tqis still zeroed (drain no-op) and the code proceeds toether_ifdetach(ifp)(:294) on the embedded ifnet that was neverif_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 isusb_proc_create()ENOMEM, whose error path self-frees theue_tq(usb_process.cusb_proc_createβusb_proc_free), so the state is branch-3-like: validue_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)
vm.sh reset with-src(clean source + clean modules), applyfix.diffin-guest to/usr/src(both hunks, Plan A).- Rebuild
uether.koandif_ipheth.koβ both-Werrorclean (fix_build.log). - Install patched
uether.kointo/boot/kernel. - Re-run the byte-identical harness (md5
f27308481209578091ab79c853df1979):ipheth_poc: SURVIVED uether_ifdetach(),KLDLOAD_RC=0,kldunloadclean,dmesg | grep -c "Fatal trap"= 0 (run.log). - Patched driver module
if_ipheth.koalso 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 appliesm_adj(ETHER_ALIGN)(usb_ethernet.c:549-560: m_data=cl+2, m_len=2046); ipheth adds a secondm_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'srm_datalen. Gatelen < 14+2frees (: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.lenclamped toIPHETH_BUF_SIZE=1516 (:422-423);usbd_m_copy_in(pc, 0, m, 0, β€1516)(:425) into frame slot x of capacity 1516 at offsetx*1516β€ 10612 < bufsize 8*1516=12128 (:415-416);usbd_frame_zero(pc, len, 1516-len)stays in-slot (:430-431); x β€ 8. Error path freessc_tx_bufbeforetr_setupre-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 intosc_data[64]+ 6-byte memcpy toue_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 stalesc_data[0]; since the value is never consumed, no impact (Info, not filed). - (d) attach/detach error paths β THE finding (above). Also checked:
lockuninitafter the panic point only;usb_proc_createfailure self-frees (label-safe for branch 4); branch-3/4ether_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.
Fix verification
fixedfix.diff applied in-guest to /usr/src (patch(1) Plan A, both files, all hunks); uether.ko and if_ipheth.ko rebuilt -Werror clean; patched uether.ko installed into /boot/kernel; the byte-identical harness re-run (md5 f27308481209578091ab79c853df1979): 'ipheth_poc: SURVIVED uether_ifdetach()' in dmesg, KLDLOAD_RC=0, dmesg|grep -c 'Fatal trap' = 0, kldunload clean, and the patched if_ipheth.ko driver module loads/unloads clean. Baseline panic record in panic.txt, patched survival in run.log + fix_smoke.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 + identical md5); fix_build.log (both modules -Werror clean); fix_smoke.log (patched if_ipheth.ko load/unload 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_ipheth.c:217-265
- sys/bus/u4b/net/if_ipheth.c:225-229
- sys/bus/u4b/net/if_ipheth.c:231-236
- sys/bus/u4b/net/if_ipheth.c:237-247
- sys/bus/u4b/net/if_ipheth.c:249-259
- sys/bus/u4b/net/if_ipheth.c:262-264
- sys/bus/u4b/net/if_ipheth.c:267-281
- sys/bus/u4b/net/if_ipheth.c:274
- sys/bus/u4b/net/if_ipheth.c:276
- sys/bus/u4b/net/if_ipheth.c:174-177
- sys/bus/u4b/net/usb_ethernet.c:133-137
- sys/bus/u4b/net/usb_ethernet.c:162-189
- sys/bus/u4b/net/usb_ethernet.c:267-305
- sys/bus/u4b/net/usb_ethernet.c:549-560
- sys/bus/u4b/net/usb_ethernet.h:80-84
- sys/bus/u4b/usb_process.c:397-410
- sys/kern/subr_bus.c:1953
Detail
Evidence (decisive lines)
panic.txt (baseline serial: 3 ipheth_poc markers -> 'Fatal user address access from kernel mode from kldload', Fatal trap 12, fault virtual address = 0x0, 'Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi', db> prompt, vm.sh status=down); run.log (patched run: SURVIVED marker in dmesg, KLDLOAD_RC=0, kldstat, UNLOAD_OK, Fatal-trap count 0, identical .ko md5 f27308481209578091ab79c853df1979); build.log (-Werror clean harness build); fix_build.log (uether.ko + if_ipheth.ko rebuilt -Werror clean after fix.diff); fix_smoke.log (patched if_ipheth.ko load/unload clean); env.txt (guest uname/kldstat/dmesg); fix.diff (git apply --check OK on clean read-only sys/ tree)
PoC changes
No seeded PoC existed. Wrote ipheth_poc.c from scratch on the DF-5486 sibling harness template: KLD module (MODULE_DEPEND uether) that kmallocs a zeroed struct ipheth_softc, replicates the only pre-failure softc init exactly (sc_iface_no=0 per :225, lockinit(&sc->sc_lock,...) per :229), prints the zeroed ue state, then replays ipheth_detach() verbatim: production usbd_transfer_unsetup(sc->sc_xfer, IPHETH_N_TRANSFER) (:274) + production uether_ifdetach(&sc->sc_ue) (:276). Built -Werror clean on the first attempt. Physical Apple-device emulation is infeasible in QEMU (no iPhone USB gadget; ipheth_attach/ipheth_detach are static); the branch-1 state + exact production call-sequence replay is the strongest runnable evidence and uses only production code after the goto (same evidence class as the 8 sibling packs).
Verified recommended fix
ipheth_attach(): route all four pre-uether_ifattach() 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/5486 fix)
Verdict
Deterministic kernel panic at USB device plug-in: ipheth_attach()'s four error branches (if_ipheth.c:235 'Cannot set alternate setting', :241 'Cannot setup USB transfers', :252 'Cannot get MAC address', :258 'could not attach interface') all goto-jump into the full ipheth_detach() (if_ipheth.c:262-263 -> :267-281) while the embedded usb_ether was never attached via uether_ifattach(). On branches 1-2 the ue_sc/ue_dev/ue_udev/ue_lock/ue_methods assignments at :243-247 have not run yet (softc is newbus M_ZERO, subr_bus.c:1953), so uether_ifdetach() (usb_ethernet.c:267-305) lockmgr()s through the NULL ue_lock β Fatal trap 12, fault VA 0x0, Stopped at lockmgr_exclusive+0x5d β after usb_proc_drain() no-ops on the zeroed ue_tq and the ifp!=NULL guard proves dead (uether_getifp() returns the embedded arpcom ifnet, usb_ethernet.c:133-137). On branches 3-4 (ue_lock valid, uether_ifattach never ran / self-freed its ue_tq via usb_proc_create's error path) the same call instead reaches ether_ifdetach() on the never-if_attach()ed embedded ifnet β the DF-2341/DF-5493 latent corruption path. The trigger is fully device-controlled: ipheth_devs[] is a product-agnostic match (:174-177), so any evil USB adapter with vendor 0x05ac exposing interface class 0xff/subclass 0xfd/protocol 1 that STALLs SET_INTERFACE(alt 1), omits the bulk endpoints, or STALLs the 0x40 GET_MACADDR vendor request panics the host at plug-in β 9th and last driver instance of the DF-5334 class (cdce/aue/axge/urndis/mos/udav/kue/cue). Verified with a KLD harness replaying ipheth_detach() verbatim (its two production calls in order) on the exact branch-1 state, using production usbd_transfer_unsetup()+uether_ifdetach(): baseline on the fresh with-src 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) ipheth_attach detach_usb label doing only unsetup+lockuninit, required for branches 3-4) both modules rebuilt -Werror clean and the byte-identical harness (md5 f27308481209578091ab79c853df1979) SURVIVED with KLDLOAD_RC=0, 0 Fatal traps, clean unload; patched if_ipheth.ko loads/unloads clean (run.log, fix_smoke.log). Fault address is fixed 0 (NULL lock pointer): panic/DoS from physical/VM USB access, no attacker-controlled data is written, no privesc path β same tier as the 8 siblings (Medium).
No comments yet.