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

qsphy_attach 'no media present' branch skips mii_phy_add_media, so miibus_mediainit calls ifmedia_set on an EMPTY media list β†’ panic("ifmedia_set") at attach (5th leaf of the DF-4930/4957/4981/5003 family, hub DF-5001)

Summary

Same family shape; qsphy sets MIIF_NOISOLATE so the generic IFM_NONE fallback is gated off - the leaf must add it itself (like truephy DF-5003). REPRODUCED on stock guest: synthetic QS6612-ID MII KLD -> 'no media present' -> panic: ifmedia_set, guest down. Explicit-IFM_NONE leaf fix (+ DF-5001 hub guard as primary).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5053 Β· 17 files
FileTypeDescriptionSize
README.md β€” 2.6 KB ↓ raw
VERDICT.md β€” 5.1 KB ↓ raw
qsmedia.c β€” 9.2 KB view raw
Makefile β€” 152 B ↓ download
build.sh β€” 128 B view raw
run.sh β€” 193 B view raw
run_fix.sh β€” 608 B view raw
build.log β€” 883 B view raw
run.log β€” 954 B view raw
run.2.log β€” 424 B view raw
run.3.log β€” 393 B view raw
panic.txt β€” 849 B view raw
kernel_build.log β€” 1.4 KB view raw
env.txt β€” 287 B view raw
fix.diff β€” 893 B view raw
manifest.json β€” 2.1 KB view raw
verdict.json β€” 5.5 KB view raw

DF-5053 β€” qsphy_attach 'no media present' branch β†’ panic("ifmedia_set") at attach (5th leaf of the DF-4930 family)

What

qsphy_attach() (sys/dev/netif/mii_layer/qsphy.c:166-174) reads BMSR once, after qsphy_reset(sc) (qsphy.c:162 β†’ mii_phy_reset writes BMCR_RESET, mii_physubr.c:191); if the media capability bits are clear it prints "no media present" (qsphy.c:171) and skips mii_phy_add_media(), then still calls MIIBUS_MEDIAINIT (qsphy.c:174) β†’ miibus_mediainit() (mii.c:227-245) walks an EMPTY ifm_list and terminates in ifmedia_set(&mii->mii_media, 0) (mii.c:244) β†’ DragonFly panic("ifmedia_set") (sys/net/if_media.c). Family: atphy DF-4930, ciphy DF-4957, jmphy DF-4981, truephy DF-5003; hub fix belongs to DF-5001. Note mii_phy_add_media's own IFM_NONE fallback is unavailable here β€” it is gated on MIIF_NOISOLATE being clear (mii_physubr.c:230) and qsphy always sets MIIF_NOISOLATE (qsphy.c:160).

Reproduce (on the QEMU guest)

# build (guest):  sh build.sh      # make SYSDIR=/usr/src/sys -> qsmedia.ko
# baseline run:   sh run.sh        # kldload -> panic; grab vm.sh log
# fixcheck:       sh run_fix.sh    # (after reset) mechanism replay
# patched kernel: apply fix.diff, make nativekernel+installkernel,
#                 reboot, kldload again -> no panic (run.3.log)

qsmedia.ko is a synthetic MII MAC controller presenting a QS6612 (PHYIDR1=0x0181 PHYIDR2=0x4401 β†’ OUI 0x006051/model 0, miidevs:66,241) whose BMSR loses its media bits across the reset the driver itself issues β€” the faulty/flaky-PHY precondition. The REAL qsphy driver probes and attaches on the synthetic bus.

Expected output

Baseline (serial console, panic.txt / run.log):

qsmedia0: <synthetic single-PHY MII controller (DF-5053)> on motherboard
qsmedia: probing MII bus (QS6612 id1=0x0181 id2=0x4401, pre-reset BMSR=0x7808)
miibus0: <MII bus> on qsmedia0
qsphy0: <QS6612 10/100 media interface> on miibus0
qsphy0:  no media present
ifmedia_set: no match for 0x0/0xfffffff
panic: ifmedia_set
Trace: ifmedia_set() <- qsphy_attach() <- device_doattach() <- bus_generic_attach() <- miibus_attach()

Patched kernel (run.3.log): same kldload, qsphy0: no media present, attach completes, NO panic, clean kldunload.

Severity rationale

Low: attach/boot-time kernel DoS requiring a PHY that answers the ID probe but reports no media capabilities after reset (faulty hardware or MDIO-in-the-loop). No unprivileged userland path drives the bus into this state. The sink itself is proven on the stock kernel with a real qsphy driver in the backtrace.

VERDICT.md
↓ download raw

DF-5053 β€” VERDICT

Status: reproduced (panic), fix validated (behavioral, patched kernel). Impact: kernel DoS at attach (Low). Confidence: certain.

The bug (path:line)

  • qsphy.c:166 β€” sc->mii_capabilities = PHY_READ(sc, MII_BMSR) & ma->mii_capmask; read after qsphy_reset(sc) (qsphy.c:162) which writes BMCR_RESET (mii_physubr.c:187-191).
  • qsphy.c:168-171 β€” if (sc->mii_capabilities & BMSR_MEDIAMASK) mii_phy_add_media(sc); else kprintf("no media present"); β€” the else branch adds NOTHING to mii->mii_media.
  • qsphy.c:174 β€” MIIBUS_MEDIAINIT(sc->mii_dev) β†’ miibus_mediainit (mii.c:227-245): parent adds nothing, ifm_list stays EMPTY, the walk (mii.c:238-243) never runs, media stays 0 (mii.c:232), and ifmedia_set(&mii->mii_media, 0) (mii.c:244) executes panic("ifmedia_set") (if_media.c).
  • 5th leaf of the atphy DF-4930 / ciphy DF-4957 / jmphy DF-4981 / truephy DF-5003 family; hub = DF-5001 (mii.c). qsphy-specific nuance: the generic mii_phy_add_media IFM_NONE fallback is gated on MIIF_NOISOLATE being CLEAR (mii_physubr.c:230-232) and qsphy always sets MIIF_NOISOLATE (qsphy.c:160), so the leaf fix must add the fallback entry itself (exactly like truephy DF-5003).

How it was reproduced

Synthetic single-PHY MII controller KLD (qsmedia.c, fakemic/DF-5009 machinery): PHYIDR1/2 = 0x0181/0x4401 (MII_OUI()=0x6051 QUALSEMI, MII_MODEL()=0 QS6612, mii.h:109-111), pre-reset BMSR = 0x7808 (passes miibus_no_phy, mii.c:113-115), and any BMCR write with BMCR_RESET latches "hosed" β†’ later BMSR reads return 0. That is precisely the hardware state the family documents (a PHY that answers probe but loses media capabilities across reset).

Stock kernel (run.log / panic.txt, serial console):

qsphy0: <QS6612 10/100 media interface> on miibus0
qsphy0:  no media present
ifmedia_set: no match for 0x0/0xfffffff
panic: ifmedia_set
ifmedia_set() at ifmedia_set+0x53
qsphy_attach() at qsphy_attach+0x159
device_doattach() ... bus_generic_attach() ... miibus_attach()

The REAL qsphy driver is in the backtrace; ssh died mid-kldload; vm.sh status β†’ down. Deterministic (the attach path is single-shot).

Fix validation (fix_status: fixed)

  1. fix.diff (leaf-level, mirrors DF-5003's approved shape): the no-media branch adds ifmedia_add(&mii->mii_media, IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), MII_MEDIA_NONE, NULL); before the MIIBUS_MEDIAINIT. Applies clean (patch -p1 Hunk #1 succeeded).
  2. Mechanism replay on a live stock guest (run.2.log, DF-5001's run_fix.sh pattern): the fixed attach shape + verbatim hub walk survives with ifm_cur non-NULL (media=0x22 = IFM_ETHER|IFM_NONE) β€” the exact invariant that keeps every *_service()'s ife->ifm_cur->ifm_media entry dereference safe.
  3. Full behavioral validation (kernel_build.log excerpt + run.3.log): fix.diff applied to guest /usr/src, make -j6 nativekernel KERNCONF=X86_64_GENERIC RC=0 (qsphy.c compiled under the full -Werror kernel flag set), installkernel RC=0, reboot into kernel #1 (Sat Sep 26 16:13:05 UTC 2026), and the exact same kldload trigger now completes: qsphy0: no media present, no panic, miibus attaches, clean kldunload. Sources reverted, md5 back to pristine (6c17ac33ff09cf4e26e4f5131b6adf7a), guest reset to the with-src snapshot.

Reachability / ceiling (honest)

Boot/attach-time DoS only. Requires the driver to attach against a PHY that matched the QS6612 ID yet reports zero media capability bits after the driver's reset β€” faulty silicon, power-transition flake, or MDIO-in-the-loop (e.g. malicious USB/emulated NIC servicing MDIO in software, if such a parent driver existed). No unprivileged syscall reaches the attach path; no memory corruption (the sink is a deliberate panic in ifmedia_set). Ceiling: reboot-loop/DoS on affected hardware.

Also checked on this file (pass-2 negatives)

  • service entry (qsphy.c:188): the CORRECT guard form (if (IFM_INST(ife->ifm_media) != sc->mii_inst)), not the DF-1973/ DF-5009 KKASSERT β€” no sibling-PHY INVARIANTS panic here.
  • the guard's isolate write (qsphy.c:190-191) ignores the driver's own MIIF_NOISOLATE (qsphy.c:160) β€” inherited NetBSD functional nit (link-loss on multi-PHY buses, no memory-safety impact); not filed.
  • status decode default β†’ IFM_NONE (qsphy.c:289-292): safe. ifm_cur->ifm_media at qsphy.c:295 is the family-wide post-attach shape (ifm_cur guaranteed by successful attach); not qsphy-specific.
  • reset waits live in mii_phy_reset (mii_physubr.c:201-209): 500us DELAY + bounded 100x1ms poll β€” correct; MII_QSPHY_IMASK write is a constant clause-22 index (0x1e, qsphyreg.h:62) β€” no OOB.
  • probe sentinel: qsphys[] NULL-terminated (qsphy.c:111); mii_phy_match stops on mpd_name==NULL (mii_physubr.c:555-563) β€” clean.

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

fixed
baseline reproduced→ patch + rebuild →patched clean

fixed: fix.diff applied to guest /usr/src; make -j6 nativekernel KERNCONF=X86_64_GENERIC RC=0 (qsphy.c compiled -Werror, kernel_build.log); installkernel RC=0; reboot into kernel #1 (Sat Sep 26 16:13:05 UTC 2026); the identical kldload trigger completes WITHOUT panic ('qsphy0: no media present', miibus attaches, clean kldunload β€” run.3.log) vs stock-kernel panic (run.log/panic.txt). Sources reverted md5-verified (6c17ac33ff09cf4e26e4f5131b6adf7a); guest reset afterwards.

fix.diff; kernel_build.log; run.log (baseline panic); run.2.log (mechanism fixcheck); run.3.log (patched-kernel behavioral run); VERDICT.md
↓ fix.diffper-fix-DF-5053

Confirmed kernel references

Detail

Evidence (decisive lines)

panic.txt + run.log (decisive: 'qsphy0:  no media present' -> 'panic: ifmedia_set' with qsphy_attach+0x159 in the trace); run.3.log (patched kernel: same kldload survives, KLDLOAD_RC=0, clean detach); run.2.log (fixcheck mechanism, ifm_cur=... media=0x22); kernel_build.log (nativekernel -Werror, qsphy.o compile line); build.log; qsmedia.c; VERDICT.md

PoC changes

Authored fresh (no seed). Adapted DF-5009's fakemic synthetic-MII-bus machinery to a single hosed QS6612: (1) the BMSR-media-lost latch keys on the driver's OWN BMCR_RESET write so the probe (miibus_no_phy, mii.c:113-115) still sees media bits; (2) QS6612 ID constants derived from miidevs OUI 0x006051/model 0 via mii.h:109-111 (PHYIDR1=0x0181, PHYIDR2=0x4401); (3) kenv hw.qsmedia.fixmode gate + hw.qsmedia_fixcheck sysctl (DF-5001's run_fix.sh pattern) so the fixed attach shape can replay on a live guest; (4) -Werror build fix (drop unused qsmedia_dev).

Verified recommended fix

In the no-media branch, add ifmedia_add(&mii->mii_media, IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, sc->mii_inst), MII_MEDIA_NONE, NULL); before MIIBUS_MEDIAINIT (per-leaf; the hub-level fix belongs to DF-5001).

Verdict

REPRODUCED with the REAL qsphy driver in the backtrace: a synthetic single-PHY MII controller KLD (qsmedia.ko) presents a QS6612 (PHYIDR1=0x0181/PHYIDR2=0x4401 -> OUI 0x6051/model 0) whose BMSR loses its media capability bits across the BMCR_RESET that qsphy_attach itself issues (qsphy.c:162 -> :166); the attach takes the 'no media present' branch (qsphy.c:168-171), skips mii_phy_add_media, and MIIBUS_MEDIAINIT (qsphy.c:174) -> miibus_mediainit walks an EMPTY ifm_list and terminates in ifmedia_set(&mii->mii_media,0) (mii.c:244) -> panic("ifmedia_set") β€” serial console: 'qsphy0: no media present / ifmedia_set: no match for 0x0/0xfffffff / panic: ifmedia_set', backtrace ifmedia_set <- qsphy_attach <- device_doattach <- bus_generic_attach <- miibus_attach; ssh died, guest down. 5th leaf of the atphy DF-4930 / ciphy DF-4957 / jmphy DF-4981 / truephy DF-5003 family (hub: DF-5001). FIX VALIDATED BEHAVIORALLY: fix.diff (ifmedia_add IFM_NONE fallback in the no-media branch, mirroring DF-5003's leaf fix; mii_phy_add_media's own fallback is unavailable because qsphy always sets MIIF_NOISOLATE, qsphy.c:160 vs mii_physubr.c:230) applied to guest /usr/src, nativekernel -Werror rebuild RC=0 (qsphy.o compiled at kbuild.log:18760), installkernel, reboot into kernel #1, and the EXACT same kldload trigger now completes with NO panic and clean kldunload; mechanism replay on stock guest (run.2.log) additionally shows ifm_cur non-NULL post-fix (the invariant keeping *_service() entry derefs safe). Sources reverted md5-verified pristine; guest reset to the with-src snapshot. Attach/boot-time DoS only: requires a PHY that matches the QS6612 ID probe yet reports zero media capabilities after reset β€” no unprivileged userland path, no memory corruption (deliberate panic sink).