# DF-5603 — card-derived zero switch-timeout panics mmc_switch()/mmc_switch_status() (live KASSERT on stock INVARIANTS kernel)

**File:** `sys/bus/mmc/mmc_subr.c` (pass 2, GLM 5.3)
**Verdict:** reproduced (impact: kernel panic / system DoS), fix validated.
**Class:** CWE-617 (reachable assertion on card-controlled data) + CWE-20.

## What

`mmc_switch()` (sys/bus/mmc/mmc_subr.c:158) and `mmc_switch_status()`
(sys/bus/mmc/mmc_subr.c:187) open with:

```c
KASSERT(timeout != 0, ("%s: no timeout", __func__));
```

On DragonFly's stock kernel this assertion is **live**: `options INVARIANTS`
ships in `sys/config/X86_64_GENERIC:56` (unlike FreeBSD GENERIC, where
KASSERT is compiled out). The `timeout` argument is not an arbitrary
programmer-supplied constant — at every call site it is `ivar->cmd6_time`,
which `mmc.c:1661-1665` derives **directly from a card-controlled EXT_CSD
byte**:

```c
ivar->cmd6_time = 500 * 1000;
if (ivar->csd.spec_vers >= 6) {
        ivar->cmd6_time = 10 * ivar->raw_ext_csd[EXT_CSD_GEN_CMD6_TIME];
}
```

A card reporting `spec_vers >= 6` (CSD is also card data) and
`EXT_CSD[EXT_CSD_GEN_CMD6_TIME] == 0` (byte 248) yields `cmd6_time == 0`,
which is then passed as the timeout to `mmc_switch()` at mmc.c:1672
(erase-group-def switch), mmc.c:709 (bus-width switch), mmc.c:785 (power
class), mmc.c:828 (timing switch) and to `mmc_switch_status()` at
mmc.c:835 — i.e. **every MMC card enumeration reaches one of these calls**,
so a crafted (or cheaply mis-programmed) eMMC/MMC card panics the kernel at
boot or on insertion.

## How to reproduce (no SD hardware needed)

`mmc` is compiled into the stock kernel (`device mmc`, guest config:324), so
a KLD harness can drive the *production* helpers directly with the exact
card-derived value (0). No SD controller is required; the two device
arguments are two distinct real devices so the helpers' error-log branch
(softc deref) is skipped and the bridge dispatch degrades to
`kobj_error_method()` → `ENXIO` (kern/subr_kobj.c:69-72).

```sh
# build (guest)
cd /tmp/df5603 && SYSDIR=/usr/src/sys make
# run (guest, as root)
kldload /tmp/df5603/df5603.ko
```

**Expected (stock kernel):** serial console shows

```
DF-5603: calling mmc_switch(timeout=0) as mmc.c does for a card with EXT_CSD[248]=0 ...
panic: mmc_switch: no timeout
mmc_switch() at mmc_switch+0x178
df5603_modevent() at df5603_modevent+0x93
...
Debugger("panic")
```

and the guest goes down (ssh hangs; `vm.sh status` ⇒ down). See `panic.txt`,
`run.log`.

**Expected (with fix.diff):** both helper calls return `4`
(MMC_ERR_FAILED) without panicking; module unloads; guest stays up. See
`run.2.log`.

## Fix

`fix.diff` — replace both KASSERTs with a 500 ms floor (the same default
mmc.c uses when the EXT_CSD field is absent) and clamp the producer in
mmc.c:1663-1666. Validated by full kernel rebuild (nativekernel) + re-run:
panic gone, clean error return.

## Files

* `df5603_harness.c` — KLD harness (production helpers, synthetic card value)
* `Makefile`, `build.sh`, `run.sh` — exact build/run
* `build.log` — full untrimmed compiler output (baseline kernel module build)
* `run.log` — baseline run narrative + panic excerpt
* `run.2.log` — patched-kernel run
* `panic.txt` — serial-console panic proof
* `env.txt` — guest identity, compiler, INVARIANTS config line
* `fix.diff` — git-apply-able unified diff (never applied to the host sys/ tree)
* `VERDICT.md`, `manifest.json`, `verdict.json`
