β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4957

ciphy_attach 'no media present' branch skips all media setup β†’ miibus_mediainit calls ifmedia_set on an EMPTY list β†’ panic("ifmedia_set") at attach on zero-capability PHYs

Summary

The DF-4930 (atphy) family shape with a CRITICAL DIVERGENCE: ciphy always sets MIIF_NOISOLATE, which GATES mii_phy_add_media's IFM_NONE fallback - the atphy one-liner fix is a no-op here; ciphy needs an explicit IFM_NONE entry (or framework hardening of miibus_mediainit for the ~20 siblings). Sink PROVEN live (panic.txt: ifmedia_set no-match -> guest down; the fixed path survives with non-NULL ifm_cur); end-to-end hw-gated (no Cicada PHY). The DF-4930 sibling.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4957 Β· 11 files
FileTypeDescriptionSize
ciphysink.c β€” 3.8 KB view raw
Makefile β€” 58 B ↓ download
build.sh β€” 98 B view raw
run.sh β€” 90 B view raw
build.log β€” 617 B view raw
run.log β€” 186 B view raw
panic.txt β€” 1.3 KB view raw
env.txt β€” 188 B view raw
fix.diff β€” 932 B view raw
VERDICT.md β€” 4.3 KB ↓ raw
verdict.json β€” 3.9 KB view raw
VERDICT.md
↓ download raw

DF-4957 VERDICT β€” ciphy no-media attach β†’ panic("ifmedia_set")

Status: NOT REPRODUCED end-to-end (no Cicada/Vitesse PHY hardware on the guest; PHY registers are not emulatable). The kernel-side sink of the chain is PROVEN live on this exact kernel β€” replaying miibus_mediainit's terminal ifmedia_set() on an EMPTY media list (what ciphy_attach's no-media branch at ciphy.c:142-147 leads to via mii.c:229-246) yields ifmedia_set: no match for 0x0/0xffffffff β†’ panic("ifmedia_set") and kills the guest (panic.txt, full trace). Fix mechanism validated live the same boot.

Root cause (source trace)

  • ciphy_attach (sys/dev/netif/mii_layer/ciphy.c:142-147): if the PHY's BMSR reports no 10/100 media bits AND EXTSR no gigabit bits, the driver prints "no media present" and skips mii_phy_add_media β€” the only routine that would populate the miibus ifmedia list.
  • ciphy.c:149 still calls MIIBUS_MEDIAINIT(sc->mii_dev) β†’ miibus_mediainit (sys/dev/netif/mii_layer/mii.c:229-246): walks the now-EMPTY ifm_list, media stays 0, calls ifmedia_set(&mii->mii_media, 0).
  • DragonFly's ifmedia_set (sys/net/if_media.c) has no no-match fallback: ifmedia_match returns NULL on the empty list β†’ kprintf("ifmedia_set: no match ...") β†’ panic("ifmedia_set"). (FreeBSD's ifmedia_set prints and returns; the panic is a DragonFly divergence.)
  • No parent of a CICADA/VITESSE PHY implements the miibus_mediainit hook (in-tree hooks exist only in if_xl/if_tx/if_dc; realistic ciphy parents are nfe/sk/vge β€” none hook it).
  • Secondary effect of the same branch: had the boot survived, ciphy_service dereferences mii->mii_media.ifm_cur (ciphy.c:156, 164, 255) which would be NULL.

Why the atphy (DF-4930) one-line fix does NOT apply here

ciphy sets sc->mii_flags |= MIIF_NOISOLATE at ciphy.c:132 BEFORE the media branch. mii_phy_add_media's IFM_NONE fallback entry is added only if ((sc->mii_flags & MIIF_NOISOLATE) == 0) (mii_physubr.c:230-232). Therefore "just call mii_phy_add_media() unconditionally" (the atphy fix) adds NOTHING for ciphy: the list stays empty and the panic remains. Every MIIF_NOISOLATE mii driver with this branch (ciphy, rgephy set NOISOLATE too, …) needs an explicit fallback entry or the shared miibus_mediainit must tolerate an empty list.

Verification on the guest

No CS8201/VSC8601 PHY exists under QEMU and MDIO register behavior cannot be emulated, so the ciphy_attach path itself could not be exercised (honest not_reproduced). The kernel-side sink WAS executed with the real kernel functions via the ciphysink KLD (adapted from the DF-4930 pack; run.sh):

  1. sysctl hw.ciphysink_fixcheck=1 β€” replays the FIXED ciphy path (ifmedia_init + explicit IFM_NONE entry + ifmedia_set): survived, ifm_cur=0xfffff8004f102640 (non-NULL, which also protects ciphy_service's deref).
  2. sysctl hw.ciphysink_fire=1 β€” replays the STOCK ciphy path (empty-list ifmedia_set):
ifmedia_set: no match for 0x0/0xffffffff
panic: ifmedia_set
ifmedia_set() at ifmedia_set+0x53
ciphysink_fire() at ciphysink_fire+0x8e
sysctl_root ... userland_sysctl ... sys___sysctl
Debugger("panic") ; guest down

This is byte-for-byte the terminal behavior the ciphy chain produces on a zero-media Cicada PHY at attach (boot-time or kldload).

Fix

fix.diff (ciphy.c): in the no-media branch, after the "no media present" print, add an explicit ifmedia_add(&mii->mii_media, IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), MII_MEDIA_NONE, NULL) β€” miibus_mediainit's ifmedia_set then finds its match, no panic, and ifm_cur is non-NULL for ciphy_service. Mechanism validated live (fixcheck above). The framework-level alternative (make miibus_mediainit skip/tolerate an empty list) would fix all ~20 sibling drivers at once and is the better long-term fix; the per-driver diff keeps this pack self-contained.

Guest was reset with-src after the panic capture (boot.log rotated); source of the panic text: serial console capture in panic.txt.

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

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff (ciphy.c: add an explicit ifmedia_add(IFM_ETHER|IFM_NONE) in the no-media branch -- NOT the atphy-shaped unconditional mii_phy_add_media call, which is a no-op here because of the MIIF_NOISOLATE gate at mii_physubr.c:230-232) could not be exercised end-to-end (no Cicada PHY hardware). Its operative mechanism WAS validated live on the same boot with the real kernel functions: hw.ciphysink_fixcheck=1 replays ifmedia_init + the explicit IFM_NONE entry + ifmedia_set and survives with ifm_cur non-NULL (panic.txt preamble), while the unfixed sequence panics two sysctls later.

panic.txt (fixcheck 'survived; ifm_cur=0xfffff8004f102640' followed by the sink panic); fix.diff; VERDICT.md 'Fix' section
↓ fix.diffper-fix-DF-4957

Confirmed kernel references

Detail

Evidence (decisive lines)

panic.txt (sink panic: 'ifmedia_set: no match for 0x0/0xffffffff' -> panic, full trace incl. ciphysink_fire; fixcheck survived with ifm_cur=0xfffff8004f102640 just before), run.log (fixcheck output then connection death on fire), VERDICT.md (ciphy.c:142-149 -> mii.c:229-246 -> if_media.c trace), fix.diff (explicit IFM_NONE fallback entry)

PoC changes

PoC authored fresh (adapted from the DF-4930 uafsink pack): renamed sysctls to hw.ciphysink_fire/fixcheck, updated the replayed chain comments to ciphy.c lines; first build failed on #include (needs generated device_if.h) -- removed the unneeded include, second build clean.

Verified recommended fix

ciphy_attach no-media branch: add explicit ifmedia_add(&mii->mii_media, IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), MII_MEDIA_NONE, NULL); (see fix.diff); or harden miibus_mediainit for empty lists tree-wide.

Verdict

NOT REPRODUCED end-to-end: the trigger requires CS8201/CS8201A/CS8201B/VSC8601 PHY hardware (ciphys[] table, ciphy.c:72-80) whose ID registers match while BMSR/EXTSR report zero media capability, and no such PHY exists on the QEMU guest (MDIO register behavior is not emulatable). The kernel-side sink of the chain is nonetheless PROVEN on this exact kernel: replaying miibus_mediainit's terminal ifmedia_set() on an EMPTY media list (what ciphy_attach's no-media branch at ciphy.c:142-147 leads to via mii.c:229-246) yields 'ifmedia_set: no match for 0x0/0xffffffff' -> panic("ifmedia_set") with trace ifmedia_set<-ciphysink_fire<-sysctl and kills the guest (panic.txt). DragonFly's ifmedia_set panics on no-match where FreeBSD returns. IMPORTANT divergence from the atphy sibling (DF-4930): ciphy always sets MIIF_NOISOLATE (ciphy.c:132) and mii_phy_add_media's IFM_NONE fallback is gated on that flag being CLEAR (mii_physubr.c:230-232), so the atphy-shaped unconditional-add fix does NOT work here; fix.diff adds an explicit IFM_NONE entry instead, and that mechanism was validated live (hw.ciphysink_fixcheck=1 survives with non-NULL ifm_cur). Boot/attach-time hardware-fault DoS, no user-controlled trigger; family precedent DF-1973/DF-4923/DF-4930, all Low.