# DF-1634 — Uninitialized heap memory in `dm_target_crypt_destroy` after failed init

## Summary
`dm_target_crypt.c:489` `priv = kmalloc(sizeof(dm_target_crypt_config_t), M_DMCRYPT,
M_WAITOK)` allocates without `M_ZERO`, so all fields are heap garbage.  Line 513
`dm_table_init_target(table_en, priv)` **publishes** `priv` into
`table_en->target_config` BEFORE any of the later error paths.  When init fails
(e.g. unsupported `iv_mode` at line 530-533 → `goto notsup`), the caller
`dm_table_load_ioctl` (`dm_ioctl.c:783-785`) calls `dm_table_destroy` →
`dm_target_crypt_destroy`, which dereferences the uninitialized fields:

- `dm_target_crypt.c:620` `dmtc_destroy_mpipe(priv)` → `mpipe_done(&priv->read_mpipe)`
  on garbage mpipe → KKASSERT / lwkt-token panic in `kern_mpipe.c` / `lwkt_token.c:458`
- `dm_target_crypt.c:628` `priv->status_str` (garbage) conditional
- `dm_target_crypt.c:633` `priv->ivgen` (garbage) conditional + dtor call
- `dm_target_crypt.c:637` `cryptoapi_cipher_freesession(priv->crypto_session)` (garbage)

## Severity / impact
- **Severity filed:** High
- **Verified impact:** `panic` (local DoS).  The first dangerous deref
  (`mpipe_done` on the garbage mpipe) trips a KKASSERT in the lwkt token layer
  (`panic: assertion "count & TOK_COUNTMASK" failed in _lwkt_reltokref`) before any
  useful corruption can land.
- **Primitive class:** uninitialized-heap deref (effectively a read/use of
  attacker-uncontrolled heap residue).  The `priv` chunk is `M_DMCRYPT`-typed; the
  residue depends on prior allocations in that zone and is not reliably shaped by
  the attacker from userspace.  On GENERIC the KKASSERT fires immediately.
- **Trigger credential:** operator group for `/dev/mapper/control`, **plus** the
  crypt init requires a writable backing block device (`vn_open FREAD|FWRITE` in
  `dm_pdev_insert`).  On default `0640 root:operator` device perms, only root can
  supply the backing device; an operator-group trigger requires the admin to have
  chmod'd a block device group-writable (a realistic dm-crypt deployment step).
  The bug itself is in the kernel code path regardless of caller; confirmed here
  by triggering as root.
- **Precondition:** admin has loaded `dm` + `dm_target_crypt` KLD modules.

## Reproduce
```sh
kldload dm
kldload dm_target_crypt
# backing device must be writable by the caller; on default perms, run as root
./build.sh
./dm_poc create
./run.sh                 # reload type=crypt with iv_mode=BOGUS
# expected (BUG): panic in mpipe_done via dm_target_crypt_destroy
# expected (FIXED): ENOTSUP (45), guest stays up
```

## Mechanism (line-accurate)
1. `dm_target_crypt.c:489` `priv = kmalloc(..., M_DMCRYPT, M_WAITOK)` — NO M_ZERO.
2. `dm_target_crypt.c:492` `dm_pdev_insert(dev)` succeeds (backing device valid).
3. `dm_target_crypt.c:498` `dmtc_find_crypto_cipher("aes","cbc",256)` → valid cipher.
4. `dm_target_crypt.c:513` `dm_table_init_target(table_en, priv)` — **publishes priv**.
5. `dm_target_crypt.c:515` `hex2key` succeeds.
6. `dm_target_crypt.c:530-533` `iv_mode="BOGUS"` not in `ivgens[]` → `goto notsup`.
   At this point: `ivgen`, `ivgen_priv`, `crypto_session`, `status_str`,
   `read_mpipe`, `write_mpipe` are **all uninitialized garbage**.
7. `dm_target_crypt.c:584-587` `notsup:` frees status_str, returns ENOTSUP.
8. `dm_ioctl.c:783` `dm_table_init` returns ENOTSUP → error path.
9. `dm_ioctl.c:785` `dm_table_destroy` → walks table entries → for the crypt entry,
   calls `target->destroy(table_en)` = `dm_target_crypt_destroy`.
10. `dm_target_crypt.c:620` `dmtc_destroy_mpipe(priv)` → `mpipe_done(&priv->read_mpipe)`
    → `lwkt_gettoken(&mpipe->token)` on garbage token →
    `panic: assertion "count & TOK_COUNTMASK" failed in _lwkt_reltokref`.

## Fix
`fix.diff`: add `M_ZERO` to the `kmalloc` at line 489.  With zero initialization,
all pointer fields are NULL and the mpipe structs are zeroed; `mpipe_done` is
explicitly documented safe on zeroed mpipe (`kern_mpipe.c:118-120`), and the NULL
checks in `dm_target_crypt_destroy` (lines 616, 628, 633) skip the garbage derefs.
Matches the finding's proposed fix.

## Fix validation
Patched `dm_target_crypt.ko` (M_ZERO fix), re-ran PoC as root: returns `ENOTSUP`
(errno 45), dmesg shows the same `iv_mode='BOGUS' unsupported` + `ENOTSUP` messages
but **no panic**, guest stays up.  The `dm_table_destroy` → `destroy` path now
safely no-ops on the zeroed fields.
