# DF-4900 — dm minor-number unit aliasing: 513th device panics the kernel (deterministic)

**File:** `sys/dev/disk/dm/dm_dev.c`

`devfs_clone_bitmap_get(&dm_minor_bitmap, 0)` (dm_dev.c:234) hands out dm
unit numbers **without any bound**, but the dev_t minor encoding
(`dkmakeminor`, sys/sys/diskslice.h:248-258 — unit bits 3-7 and 21-24, i.e.
**9 bits = DKMAXUNITS = 512**) cannot represent units ≥ 512:
`disk_create_named(name, unit, …)` → `make_only_dev(…, dkmakewholedisk(unit), …)`
(subr_disk.c:700) and `dmv->minor = minor(dmv->devt)` (dm_dev.c:261).

From the **513th simultaneously-allocated unit** on, the new device's minor
aliases (mod 512) the minor of a **live** dm device. `dm_dev_insert` then
looks up `_dm_dev_lookup(dev->name, NULL, dev->minor)` (dm_dev.c:191) — the
by-minor walk (dm_dev.c:129-131) HITS the live device, the else-branch is
taken with `dmv == NULL`, and `KKASSERT(dmv != NULL)` (dm_dev.c:195)
panics. Same assert site as DF-2442, but **race-free**: 513 sequential
`dmsetup create <uniq> --notable` ioctls, no concurrency needed.

Secondary consequences (post-assert / on builds without KKASSERT):
- `dkunit()` (diskslice.h:318-324) decodes only the 9-bit unit field, so
  destroying a unit-≥512 device feeds the *aliased live device's* unit to
  `devfs_clone_bitmap_put` (dm_dev.c:285,290) — the live device's clone bit
  is freed and can be double-allocated.
- duplicate live minors → every by-minor path (dmopen/dmclose `is_open`
  tracking, dm_dev_remove/status/suspend/resume/table ioctls) operates on
  the FIRST device in the TAILQ, not the one addressed.

## Reproduce (as run on the audit guest)
```
scp poc_phase1.sh poc_phase2.sh dfbsd:/root/
ssh dfbsd 'sh /root/poc_phase1.sh'   # 512 devices: all succeed, minors distinct
ssh dfbsd 'sh /root/poc_phase2.sh'   # 513th unit -> kernel panic
```
Expected: phase 1 prints `xa`/`ya` with the SAME recycled minor
(0x1e110007) and `phase1 OK`; phase 2 panics:
`panic: assertion "dmv != NULL" failed in dm_dev_insert at .../dm_dev.c:195`
(panic.2.txt; first accidental observation in panic.accidental-run.txt).

## Fix validation
`fix.diff` bounds the allocation (`devfs_clone_bitmap_get(&dm_minor_bitmap,
DKMAXUNITS - 1)`, ENOSPC rollback) and masks the put (`minor &
(DKMAXUNITS-1))`). Module-only rebuild in the guest (build.log):
baseline panics on the 513th create; patched module refuses it cleanly
(`513th create refused`) and the guest stays up (fix_run.log).
