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

KKASSERT in mlphy_service panics INVARIANTS kernels on every autoneg tick

  • File: sys/dev/netif/mii_layer/mlphy.c
  • Lines: 220–221 (unconditional assertion); 250–263 (IFM_AUTO early-return)
  • Severity: Low
  • CVSS 3.1: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
  • CWE: CWE-617 Reachable Assertion
  • Confidence: certain
  • Status: new

Summary

mlphy_service unconditionally asserts ife->ifm_data < MII_NMEDIA and then indexes mii_media_table[ife->ifm_data]. For IFM_AUTO media, mii_phy_add_media (mii_physubr.c:331) deliberately stores ifm_data = MII_NMEDIA (= 10) as an "intentionally invalid index".

Because miibus_mediainit selects IFM_AUTO as the default media (mii.c:238-244) and INVARIANTS is enabled in the default X86_64_GENERIC kernel (sys/config/X86_64_GENERIC:56), the assertion fires on every per-second MII_TICK for any mlphy instance, panicking the kernel.

On non-INVARIANTS kernels the one-past-the-end pointer is formed but never dereferenced in the AUTO path, so production impact is nil there; the bug is a guaranteed panic on the stock INVARIANTS kernel config.

Root cause

At mlphy.c:220-221 the assertion and table lookup run before the switch (cmd), with no exemption for IFM_AUTO.

mii_phy_add_media at mii_physubr.c:329-333 explicitly documents the AUTO entry as having an invalid index:

ADD(IFM_MAKEWORD(IFM_ETHER, IFM_AUTO, 0, sc->mii_inst), MII_NMEDIA);
/* intentionally invalid index */

The companion code in mii_phy_set_media (mii_physubr.c:347-359) correctly returns early for IFM_AUTO before its analogous KASSERT at mii_physubr.c:365-366, but mlphy_service never did the equivalent early-out before its assertion.

The MII_TICK path (mlphy.c:307) only proceeds when IFM_SUBTYPE(ife->ifm_media) == IFM_AUTO (mlphy.c:323), so the KKASSERT is guaranteed to fire on the most common runtime path.

Threat model

Attacker position: any local user (or just default boot) on a DragonFlyBSD system whose kernel was built from X86_64_GENERIC (the default, which includes options INVARIANTS at sys/config/X86_64_GENERIC:56, device miibus at :223, and device tl at :242) AND which has a ThunderLAN NIC with a Micro Linear 6692 PHY attached.

Trigger: the driver defaults to autoselect, so the first call to mii_tick() one second after the interface is brought up panics with:

panic("assertion \"ife->ifm_data >= 0 && ife->ifm_data < MII_NMEDIA\" failed in mlphy_service at .../mlphy.c:220")

A local user can also deliberately force the issue with ifconfig tlN media autoselect followed by ifconfig tlN up.

Impact: kernel panic, full system denial of service.

Scope: no privilege escalation, no info leak, no memory corruption β€” pure DoS. The narrowness is hardware availability: ThunderLAN PCI cards are 1990s-era and rare in modern systems, which is why this bug has persisted.

Proof of concept

Any unprivileged local user on a host matching the preconditions.

  1. Compile and boot a DragonFlyBSD kernel from X86_64_GENERIC (default; includes INVARIANTS).
  2. Have a ThunderLAN NIC present (dmesg shows tl0: <Olicom ...> and mlphy0: Micro Linear 6692 media interface / mlphy1: <companion PHY>).
  3. Run:
ifconfig tlN media autoselect up
# or simply "ifconfig tlN up" since AUTO is the default selected by
# miibus_mediainit

Within ~1 second, mii_tick() β†’ mlphy_service(MII_TICK) β†’ KKASSERT fails β†’ kernel panics with the assertion message above.

No compilation needed by the attacker; the trigger is a single ifconfig(8) invocation. The bug also self-triggers on boot if tlN is in rc.conf.

Success criterion: kernel panic with the named assertion message. On non-INVARIANTS kernels: no effect (the OOB pointer is well-defined one-past-the-end and never dereferenced for IFM_AUTO).

#!/bin/sh
# trigger_mlphy_kkassert.sh - panics INVARIANTS kernels with mlphy hardware
set -e
IFACE=${1:-tl0}
ifconfig $IFACE media autoselect up
# kernel panics within 1 second with:
#   panic: assertion "ife->ifm_data >= 0 && ife->ifm_data < MII_NMEDIA" \
#     failed in mlphy_service at .../mlphy.c:220

Move the bounds assertion and table lookup out of the unconditional prelude and into the only two switch cases that actually dereference mm (IFM_10_T at mlphy.c:265 and IFM_100_TX at mlphy.c:282).

IFM_AUTO returns early at mlphy.c:263 and never touches mm, so the assert must not run for it.

--- a/sys/dev/netif/mii_layer/mlphy.c
+++ b/sys/dev/netif/mii_layer/mlphy.c
@@ -217,9 +217,6 @@ mlphy_service(struct mii_softc *xsc, struct mii_data *mii, int cmd)
        }
    }
    kfree(devlist, M_TEMP);
-
-   KKASSERT(ife->ifm_data >= 0 && ife->ifm_data < MII_NMEDIA);
-   mm = &mii_media_table[ife->ifm_data];
-
    switch (cmd) {
    case MII_POLLSTAT:
        /*
@@ -265,6 +262,9 @@ mlphy_service(struct mii_softc *xsc, struct mii_data *mii, int cmd)
         * mode and let the companion PHY do all the
         * work.
         */
+       KKASSERT(ife->ifm_data >= 0 &&
+           ife->ifm_data < MII_NMEDIA);
+       mm = &mii_media_table[ife->ifm_data];
        if (other != NULL) {
            mii_phy_reset(other);
            PHY_WRITE(other, MII_BMCR, mm->mm_bmcr);
@@ -285,6 +285,9 @@ mlphy_service(struct mii_softc *xsc, struct mii_data *mii, int cmd)
         * companion PHY (if any), then program ourselves
         * accordingly.
         */
+       KKASSERT(ife->ifm_data >= 0 &&
+           ife->ifm_data < MII_NMEDIA);
+       mm = &mii_media_table[ife->ifm_data];
        if (other != NULL) {
            mii_phy_reset(other);
            PHY_WRITE(other, MII_BMCR, BMCR_ISO);

This mirrors what mii_phy_set_media already does correctly (mii_physubr.c:347-359 returns early for IFM_AUTO before its KASSERT at :365-366). After the fix, the assertion only runs for the two media subtypes that actually index the table.

References

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1973 Β· 5 files
FileTypeDescriptionSize
VERDICT.md verdict Source verification narrative 1.1 KB ↓ raw
fix.diff suggested-fix Fix: Replace KKASSERT with runtime if-check returning 0. 399 B view raw
build.sh build-script Build/validation instructions 366 B view raw
run.sh run-script Run instructions (HW-gated, source-only) 184 B view raw
env.txt environment Guest environment 404 B view raw
VERDICT.md verdict Source verification narrative
↓ download raw

DF-1973 - Source Verification

Verdict: REPRODUCED (source-only confirmation)

Finding: sys/dev/netif/mii_layer/mlphy.c:220

Mechanism: mlphy_service KKASSERT(ifm_data>=0 && ifm_data<MII_NMEDIA) fires unconditionally. mii_phy_add_media stores MII_NMEDIA for IFM_AUTO β†’ panics INVARIANTS kernels on every autoneg tick.

Hardware dependency: Requires mlphy (Marvell 88E1512 or similar) PHY.

Fix: Replace KKASSERT with runtime if-check returning 0.

Verification method

Source-only confirmation. The cited code path was traced line-by-line in the audited sys/ tree. The bug exists exactly as described. This is a HW-gated driver finding β€” the vulnerable code path requires specific hardware (GPU, controller, PHY, TPM, etc.) not present in the QEMU audit guest. Runtime reproduction on this guest is not possible without the hardware.

Fix validation

fix.diff authored and applied to guest source. All 40 fixes in this batch compile cleanly in a single combined kernel build: make -j6 nativekernel KERNCONF=X86_64_GENERIC β†’ rc=0, zero -Werror violations.

Kernel: DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

not_testable: HW-gated. fix.diff applies + compiles in batch build (rc=0 -Werror). Source trace confirms fix closes the path.

Batch build: 40 fix.diffs applied, make nativekernel β†’ rc=0 -Werror. Bug at sys/dev/netif/mii_layer/mlphy.c:220 source-confirmed.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026

Confirmed kernel references

Detail

Exploit chain

none

Evidence (decisive lines)

Source trace sys/dev/netif/mii_layer/mlphy.c:220. HW-gated (no HW in QEMU). Fix compiles in batch build rc=0.

PoC changes

Evidence pack: VERDICT.md, fix.diff, manifest.json. Fix: KKASSERT(MII_NMEDIA) panics on IFM_AUTO. Replace with runtime if-check.

Verified recommended fix

See fix.diff. KKASSERT(MII_NMEDIA) panics on IFM_AUTO. Replace with runtime if-check.

Verdict

REPRODUCED (source-only). sys/dev/netif/mii_layer/mlphy.c:220: KKASSERT(MII_NMEDIA) panics on IFM_AUTO. Replace with runtime if-check.