# DF-2449 — dm_table_load_ioctl uninitialized start/length leak

## Bug (sys/dev/disk/dm/dm_ioctl.c, dm_table_load_ioctl)

`dm_table_load_ioctl` allocates `dm_table_entry_t` with
`kmalloc(sizeof(dm_table_entry_t), M_DM, M_WAITOK)` — NO `M_ZERO` — then
fills `table_en->start` / `table_en->length` via
`prop_dictionary_get_uint64()` whose return value is **never checked**.
`prop_dictionary_get_uint64` returns `false` WITHOUT writing `*valp` when
the key is absent (`sys/libprop/prop_dictionary_util.c:126` early-return
before the line-136 `*valp = ...` assignment), so omitting `"start"` /
`"length"` from the per-table-entry dict leaves the two `uint64_t` fields
at whatever stale slab bytes `kmalloc` returned. Those bytes are then
leaked verbatim to userspace by `dm_table_status_ioctl`
(`sys/dev/disk/dm/dm_ioctl.c:937-940`). 16 bytes per entry per query.

## Files

* `dm_uninit_startlength.c` — the PoC.
* `build.sh`, `run.sh` — exact build / run.
* `fix.diff` — git-apply-able fix (M_ZERO + checked get_uint64 → EINVAL).
* `VERDICT.md` — full narrative, mechanism, validation.
* `manifest.json` — artifact catalog.

## Reproduce

```sh
./build.sh && ./run.sh
```

(as root, after `kldload dm`).

To make the leak visible on a quiet slab, enable the INVARIANTS debug
pattern first (otherwise trial 0 may return 0/0 because the slab's
underlying page came from the pre-zeroed pool):

```sh
sysctl -w debug.use_malloc_pattern=1
sysctl -w debug.use_weird_array=1
./run.sh
```

## Expected

* **Unpatched kernel** (`6.5-DEVELOPMENT #0`): trial 0 prints
  `start=0x0000000000000000 length=0x0000000000000000`; trials 1-4 print
  `start=0xffffffffffffffff length=0xffffffffffffffff` (with the
  `use_malloc_pattern` sysctl on) — variance across trials is the leak
  signature. Without the sysctl, the values are real slab residue.
* **Patched dm.ko**: every reload returns `EINVAL (22)`; the PoC prints
  `FIX IS IN EFFECT: no uninitialized-heap leak.` A valid reload that
  DOES supply start/length still succeeds and read-back matches.

## Impact

`/dev/mapper/control` is `0640 root:operator`; `dm` module load is
root-only. Reachable only by root / operator group — **root/operator →
kernel info leak** (CWE-457 / CWE-908), not unpriv→root. No write
primitive, so no escalation chain (valid Phase-6 hard blocker).
