⬢ DragonFlyBSD Kernel Audit
DF-5603 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5603",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Card-derived zero switch timeout panics the stock DragonFly kernel: mmc.c:1661-1665 copies EXT_CSD[EXT_CSD_GEN_CMD6_TIME] (byte 248, read verbatim from the card at mmc.c:1619) into ivar->cmd6_time for cards reporting CSD spec_vers >= 6, with no floor; every enumeration then passes it to mmc_switch()/mmc_switch_status() (mmc.c:1672/:709/:785/:828/:835), whose KASSERT(timeout != 0) at mmc_subr.c:158/:187 is a live panic because options INVARIANTS ships in X86_64_GENERIC (config:56). A crafted or mis-programmed MMC card (EXT_CSD[248]=0) therefore crashes the kernel at boot/insertion. Reproduced on the stock guest kernel with a production-code KLD harness driving the compiled-in helpers with the exact card-derived value: panic: mmc_switch: no timeout, guest down, DDB on serial. Not memory corruption - no uid=0 route; the ceiling is a whole-system DoS requiring a crafted physical card (or sdhci-capable malicious hardware).",
  "exploit_chain": "crafted eMMC/MMC card with CSD spec_vers>=6 and EXT_CSD[248]=0 -> card enumeration (mmc_discover_cards) -> mmc.c:1663 cmd6_time=0 -> mmc_switch(...,timeout=0,...) [mmc.c:1672 or :709/:828; mmc_switch_status at :835] -> KASSERT at sys/bus/mmc/mmc_subr.c:158/:187 -> panic, all CPUs stopped, DDB. Boot-device variant = persistent boot loop. No escalation beyond DoS (assertion, not a write primitive).",
  "evidence": "panic.txt: serial-console proof (panic: mmc_switch: no timeout, stack mmc_switch<-df5603_modevent<-linker_load_module); run.log: ssh hang (exit 124) + vm.sh status=down right after kldload; run.2.log + dmesg markers: patched kernel runs both helper calls (return 4 = MMC_ERR_FAILED), module unloads, guest stays up; build.log: full KLD build output; env.txt: uname X86_64_GENERIC, config:56 INVARIANTS, gcc 8.3.",
  "kernel_refs": [
    "sys/bus/mmc/mmc_subr.c:158",
    "sys/bus/mmc/mmc_subr.c:187",
    "sys/bus/mmc/mmc.c:1661",
    "sys/bus/mmc/mmc.c:1663",
    "sys/bus/mmc/mmc.c:1672",
    "sys/bus/mmc/mmc.c:709",
    "sys/bus/mmc/mmc.c:828",
    "sys/bus/mmc/mmc.c:835",
    "sys/sys/systm.h:94",
    "sys/config/X86_64_GENERIC:56",
    "sys/kern/subr_kobj.c:69"
  ],
  "poc_changes": "No seeded PoC existed (pass-2 new finding). Harness authored from scratch: KLD calling the production compiled-in mmc_switch()/mmc_switch_status() with timeout=0; uses two distinct real devices so the softc-deref error-log branch is skipped (brdev!=reqdev at mmc_subr.c:95/:139) and bridge dispatch degrades to kobj_error_method()->ENXIO; kernel printf is kprintf in DF; Makefile needs SRCS+=bus_if.h device_if.h. Fix validation required nativekernel (snapshot lacks buildworld stamp) and surviving the /tmp wipe on reboot.",
  "attempts": 2,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (baseline); patched-fix kernel #1: Thu Oct  8 12:55:40 UTC 2026",
  "runtime_sec": 700,
  "guest_dirty": 0,
  "build_cmd": "cd /tmp/df5603 && SYSDIR=/usr/src/sys make",
  "run_cmd": "kldload /tmp/df5603/df5603.ko",
  "code_hash": "f3441881c08313711aa0e899f7538663884417460c986a77895583aaac8271f8",
  "notes": "Reserved-ID block DF-5603..DF-5615; only DF-5603 used (one new pass-2 finding; the rest of the block unused). mmc.c leads recorded in VERDICT.md section 5 for mmc.cs re-queued pass: (1) 10*GEN_CMD6_TIME stores ms into a [us] field vs sdhcis us max_busy_timeout (sdhci.c:1877-1883 1 s=1000000) -> ~1000x short poll budget, spurious MMC_ERR_TIMEOUT on slow cards, shared with FreeBSD head; (2) card-controlled SEC_CNT -> disk geometry unbounded (mmc.c:1627); (3) EXT_CSD used without REV cross-check (mmc.c:1619); (4) missing upstream CRC-retry loop + retune_paused bracketing (robustness); (5) DF-1115s fix should match FreeBSD heads (status & R1_SWITCH_ERROR) != 0.",
  "recommended_fix": "Floor the card-derived timeout instead of asserting on it: replace both KASSERT(timeout != 0) in sys/bus/mmc/mmc_subr.c with if (timeout == 0) timeout = 500 * 1000; and clamp the producer at mmc.c:1663 (cmd6_time == 0 -> 500 * 1000).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (both helper KASSERTs -> 500 ms floor + producer clamp in mmc.c:1663) validated by full guest kernel rebuild (make -j6 nativekernel KERNCONF=X86_64_GENERIC, installkernel, reboot into #1 Oct 8 12:55) and re-run of the identical PoC: baseline panic mmc_switch: no timeout GONE; mmc_switch/mmc_switch_status both returned MMC_ERR_FAILED (4) through the normal error path, module unloaded cleanly, guest stayed up. fix_patched_reproduced=0 is the desired outcome (bug no longer reproduces on the patched kernel).",
  "fix_evidence": "run.2.log (kldload rc=0, unload rc=0, vm.sh status=up) + dmesg markers in VERDICT.md section 4 (returned 4 WITHOUT panicking for both helper sites); baseline for comparison in panic.txt/run.log (panic, guest down)."
}