# DF-5769 — VERDICT

**Finding:** `mmc_discover_cards()` has no host-side termination — a card
that keeps answering CMD2 (ALL_SEND_CID) loops the enumerator forever and
(fresh CIDs) allocates one `mmc_ivars` + one bus child per iteration
without bound.
**File:** `sys/bus/mmc/mmc.c:1413-1710` (loop head `1413`, exits only at
`1417-1422`; alloc `1440-1441`; child add `1704-1707`).
**Verdict: REPRODUCED — unbounded kernel-memory consumption / permanent
wedge (DoS); fix validated on a patched kernel.**

## 1. Root cause

`mmc_discover_cards()` enumerates `while (1)`; the ONLY loop exits are
CMD2 errors/timeouts (`mmc.c:1417-1422`) and per-card error `break`s.
Per JEDEC, a real card exits the identification state (stops answering
CMD2) once it owns an RCA, so termination is delegated to the card.
A crafted card that keeps answering CMD2:

* **mutating CID** — the CID-match loop (`mmc.c:1426-1432`) never hits,
  each pass is a "new card": `kmalloc(sizeof(struct mmc_ivars))`
  (`mmc.c:1440-1441`, ~0.7 kB M_DEVBUF) + `device_add_child`
  (`mmc.c:1704-1707`, unnamed child — no unit ceiling, no devclass cap)
  per iteration, forever. The children are only attached (and only
  become freeable) at `bus_generic_attach` (`mmc.c:1908`) — which is
  never reached because discovery never returns.
* **static CID** — the same card is re-enumerated forever
  (`ivar->rca = rca++` per pass, `mmc.c:1572`, uint16_t wrap) — non-
  terminating even without memory growth.

Consequences: the discovery context (config_intrhook /
`mmc_delayed_attach`, `mmc.c:1976-1982`) is wedged forever; the mmc bus
is permanently unusable; `kldunload` is impossible; kernel memory is
consumed monotonically. Reachable at boot / card insertion / resume by
any malicious MMC-mode card (the SD probe is made to fail → `mode_mmc`,
`mmc.c:1779-1786`; the harness does exactly that).

## 2. Reproduction (no SD hardware)

Same harness family as DF-5768 (production `mmc.c` compiled into the
KLD, fake `mmcbr` bridge) built with `DFH_MUTATING_CID`: every CMD2
answered with success and a fresh CID.

Baseline (stock kernel, clean snapshot):

```
DFH: MOD_LOAD (variant: DF-5769 mutating-CID)
DFH: boot thread: creating dfhbr0
mmc0: <MMC/SD bus> on dfhbr0          <- production discovery now loops forever

vmstat -m (M_DEVBUF "in-use"):
  fresh boot:                       102K
  t+~6 min:                        89.1M   (+ ~110,000 ivar+child allocations)
  t+~7.5 min:                      94.1M   (monotonic, unbounded)
```

* the monitored ssh session timed out (guest saturated); ~12 min after
  load the guest stopped accepting new ssh sessions entirely (serial
  console alive — pure wedge/exhaustion, no crash);
* `kldunload` impossible (the never-returning scan holds the module);
* there is NO exit condition in production code for this card behavior
  (verified by trace; the O(N²) CID-match/`device_get_children` cost at
  `mmc.c:1424-1433` slows the loop over time but never stops it).

Honest scope note: on the 4 GB test guest the leak rate (after the O(N²)
slowdown sets in) means full OOM takes hours; the demonstrated,
code-certain property is *unbounded* growth + *non-termination* +
permanent bus wedge from a single card insertion. `impact=dos`.

## 3. Fix validation

`fix.diff` bounds the loop host-side: `while (iter++ < 32)` at
`mmc.c:1415`. Applied in the guest's `/usr/src` only (together with
DF-5768's hunks — both in mmc.c, non-overlapping); rebuilt kernel
`#1: Sun Oct 11 18:02:40 UTC 2026`; harness rebuilt against patched
source; **identical PoC re-run**:

```
kldload rc=0
mmcsd0 .. mmcsd31 attach            <- exactly 32 cards, then discovery STOPS
mmcsd31: reading primary partition table: error accessing offset ... (fake
          card refuses reads -> normal production error path, no crash)
kldunload rc=0
vmstat -m: devbuf 227K              <- all card structures reclaimed
```

Guest healthy and responsive throughout; baseline wedge GONE.

## 4. Impact / threat

Malicious SD/MMC card at insertion/boot/resume → unbounded kernel memory
consumption and a permanently wedged mmc subsystem (and, on the static-
CID variant, an infinite non-allocating rediscovery loop that still
blocks all card I/O). CWE-400 + CWE-835. DoS, not memory corruption.

## 5. Environment

See `env.txt`; guest restored to the clean `with-src` snapshot after the
runs.

## 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.
