⬢ DragonFlyBSD Kernel Audit
DF-5769 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5769",
  "status": "reproduced",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "verdict": "mmc_discover_cards()s while(1) (mmc.c:1413) exits only on CMD2 failure or per-card errors (mmc.c:1417-1422) - termination is delegated entirely to the card, which per JEDEC stops answering CMD2 after RCA assignment. A crafted card that keeps answering CMD2 with fresh CIDs makes every iteration a new card: kmalloc(mmc_ivars) (mmc.c:1440-1441) + device_add_child (mmc.c:1704-1707) per iteration, forever, never freed (attach only happens at mmc.c:1908, never reached); with a static CID the same card is re-enumerated forever (ivar->rca = rca++ per pass, mmc.c:1572). Reproduced end-to-end with the production-mmc.c KLD harness (mutating-CID fake card): M_DEVBUF grew 102K -> 89.1M -> 94.1M monotonically (~110k allocations in ~8 min, still climbing), the discovery thread never terminated, kldunload became impossible, the mmc bus wedged permanently, and the guest stopped accepting ssh sessions ~12 min after insertion. Fix validated on a rebuilt kernel: while (iter++ < 32) host-side bound -> exactly 32 cards enumerated, clean kldunload, all memory reclaimed (devbuf 227K), guest healthy.",
  "exploit_chain": "malicious MMC-mode card (SD probe fails -> mode_mmc) -> answers every CMD2 with success + fresh CID -> mmc_discover_cards() never exits -> unbounded kmalloc(mmc_ivars)+device_add_child -> kernel memory exhaustion + permanently wedged mmc bus + unloadable module + dead discovery context. Static-CID variant: infinite re-enumeration (rca++ wrap) with the same non-termination. Impact ceiling: denial of service (no memory-corruption primitive; allocations are zeroed and never reused).",
  "evidence": "run.log (vmstat -m samples: devbuf 102K -> 89.1M -> 94.1M; ssh timeouts; serial console shows discovery starting and never returning); fix.log (patched kernel: mmcsd0..mmcsd31 then STOP, kldunload rc=0, devbuf 227K); VERDICT.md; dfh_harness.c (DFH_MUTATING_CID); build.log",
  "kernel_refs": [
    "sys/bus/mmc/mmc.c:1413",
    "sys/bus/mmc/mmc.c:1415",
    "sys/bus/mmc/mmc.c:1417",
    "sys/bus/mmc/mmc.c:1426",
    "sys/bus/mmc/mmc.c:1440",
    "sys/bus/mmc/mmc.c:1572",
    "sys/bus/mmc/mmc.c:1704",
    "sys/bus/mmc/mmc.c:1908",
    "sys/bus/mmc/mmc.c:1976"
  ],
  "poc_changes": "Same harness family as DF-5768 (production mmc.c compiled into the KLD, fake mmcbr bridge); the mutating-CID variant is selected by a #define baked into the source copy (a Makefile CFLAGS+= -D flag proved unreliable under DFs kmod makefile - caught because the first build silently took the static path and hit the DF-5768 panic instead; variant markers added to MOD_LOAD output to make the active variant self-evident). Monitoring moved to periodic vmstat -m sampling because the wedge kills the controlling ssh session.",
  "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",
  "runtime_sec": 1980,
  "guest_dirty": 0,
  "build_cmd": "cd /tmp/df5769 && SYSDIR=/usr/src/sys make",
  "run_cmd": "sh /tmp/df5769/run.sh   # kldload in background + vmstat -m sampling; expect unbounded devbuf growth, ssh death, wedged guest",
  "code_hash": "a3b54c3059b4053bbcb6932b56aab8e511935c853b5554d99cd7be797cdf37fc",
  "notes": "Honest scope: on the 4 GB test guest full OOM takes hours once the O(N^2) CID-match cost (mmc.c:1424-1433) throttles the loop; the code-certain properties demonstrated are unbounded monotonic allocation, absence of any host-side termination condition, permanent bus wedge and impossible unload. Fix validated on the same rebuilt kernel as DF-5768 (both hunks in the guest /usr/src copy only). Guest reset to the clean with-src snapshot after the runs.",
  "recommended_fix": "Bound the enumeration loop host-side: while (iter++ < 32) at mmc.c:1413 (32 cards is already generous for one MMC/SD bus) - see fix.diff, validated end-to-end (32 cards then stop, clean unload, memory reclaimed).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched kernel (kernel #1 Sun Oct 11 18:02:40 UTC 2026) with the 32-iteration bound at mmc.c:1415: identical PoC enumerates exactly mmcsd0..mmcsd31 and STOPS, the fake disks error through the normal production read-failure path (no crash), kldunload rc=0, devbuf returns to 227K, guest responsive throughout. Baseline unbounded growth/wedge eliminated.",
  "fix_evidence": "fix.log (patched-run output: mmcsd28..31 partition-probe errors then clean kldunload + devbuf 227K); fix.diff (git apply --check clean against sys/)"
}