# DF-2216 — `dmi_match()` leaks the `kgetenv()` buffer on every code path

**Status: REPRODUCED (verbatim-code, kernel-compiled rig)** — impact class:
unbounded kernel-heap (M_KENV) exhaustion = availability only. **Fix: A/B-validated
in the same harness kernel** (`fix.diff`, `kfreeenv(s)` on every arm).

Part of the DF-22X batch (DF-2216 / DF-2220 / DF-2223 / DF-2224) verified in ONE
harness kernel; this pack documents the DF-2216 section.

## What this pack contains

| file | role |
|---|---|
| `rig_head.c`, `rig_mid.c`, `rig_tail.c` | rig sources (shared by the batch; sha-pinned below) |
| `fix.diff` | git-apply-able unified diff against `sys/dev/drm/linux_dmi.c` |
| `build.sh` | provenance chain (sha256 gates on the four audited tree files, verbatim `kvasprintf` extraction, fix.diff-applied fixed variants, single harness kernel DFV22X) |
| `run.sh` | triggers the rig (`sysctl kern.df22x_run=1`) and extracts dmesg |
| `build.log` | FULL untrimmed nativekernel build transcript |
| `run.log` | FULL decisive rig output (all four findings) |
| `run_section.log` | the DF-2216 lines only |
| `run.first_meterbug.log` | first iteration: meter double-counted `ks_loosememuse` (up-only fold counter) — kept as the engineering record of why the meter sums only `ks_use[].memuse` |
| `env.txt` | guest environment + all provenance hashes |
| `VERDICT.md`, `manifest.json`, `verdict.json` | verdict narrative + machine-readable results |

## How to reproduce

On the DragonFly audit guest (root, `/usr/src` = clean-source snapshot):

```sh
# push this directory to the guest as /root/poc/df22x (all batch files)
sh build.sh      # builds harness kernel DFV22X (nativekernel, ~20 min cold)
cp /usr/obj/usr/src/sys/DFV22X/kernel.stripped /boot/kernel/kernel
cp /usr/obj/usr/src/sys/DFV22X/kernel.debug   /boot/kernel/kernel.debug
shutdown -r now
sh run.sh        # sysctl kern.df22x_run=1; extracts the DF22X dmesg section
```

## Provenance (checked at build time, build aborts otherwise)

1. guest tree `linux_dmi.c` sha256 `c0f7f7a5...` == audited `sys/` tree file.
2. the rig `#include`s the tree file **verbatim** — `dmi_match` in the kernel
   is the production function, unmodified.
3. `dmi_match_fixed` = the same bytes + `fix.diff`, def renamed only
   (`fixed_dmi.c` sha256 `76433ec6...`).
4. assembled rig `df_22x_rig.c` sha256 `c646506a...` (recorded in env.txt).

## Expected output (baseline = unmodified production code)

```
DF2216 CONTROL  paired kgetenv+kfreeenv x16384: memuse delta=0 calls delta=16384
DF2216 BASELINE dmi_match x16384 (matchret=1 mismatchret=0): memuse delta=1703936 ... leaked-bytes-per-call=104
DF2216 BASELINE arm smbios.system.maker   single call: memuse delta=104   (x4 arms)
DF2216 FIXED    dmi_match_fixed x16384 (...): memuse delta=0 calls delta=16384
DF2216 VERDICT: baseline leaked 1703936 bytes in 16384 calls (control 0, fixed 0) -> LEAK CONFIRMED, FIX VALID
```

The meter is the kernels own per-type accounting (`malloc_type.ks_use[].memuse`,
aggregated exactly as `kern_slaballoc.c:451` does).

## Attacker input / reachability

`dmi_match()` has exactly one in-tree caller: `radeon_atom_pick_dig_encoder()`
(`sys/dev/drm/radeon/atombios_encoders.c:2251`), gated by `ASIC_IS_DCE32`
(radeon HD4000-class). It runs during encoder assignment — display init, DP/HDMI
hotplug, mode sets — reachable by a user holding the DRI device node. Each call
leaks one `kmalloc(strlen(value)+1, M_KENV)` buffer (~104 B slab-quantized);
there is no bound. No corruption primitive, no info disclosure → no privesc
route; ceiling is kernel-heap exhaustion (DoS).
