DF-5769 / verdict.json
{ "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/)" } |