# DF-4900 VERDICT — reproduced (deterministic kernel panic), fix validated

## What was observed (audit guest, DragonFly 6.5-DEVELOPMENT X86_64_GENERIC)
1. **Baseline, controlled run** (run.log + panic.2.txt):
   - Part A: `dmsetup create xa --notable` → minor `33,0x1e110007`
     (= dkmakewholedisk(0) = slice 1 + part 0xFF bits + unit 0 — predicted
     exactly from diskslice.h before running); `remove xa`; `create ya` →
     the SAME minor — clone bitmap round-trip is exact below the 512-unit
     boundary.
   - Part B: 512 sequential creates (units 0..511) all succeed with
     distinct minors (`d0511` = `0x1ff100ff` = dkmakewholedisk(511)).
   - Phase 2: with those 512 live, the **next** create allocates unit 512,
     whose encoded minor is identical to the live unit-0 device's
     (`dkmakeminor` unit field is 9 bits: `(512&0x1f)=0`, `(512&0x1e0)=0`).
     `dm_dev_insert` (dm_dev.c:191) walks by minor, hits the live device,
     falls into the else-branch with `dmv==NULL`, and `KKASSERT(dmv != NULL)`
     at dm_dev.c:195 fires:
     `panic: assertion "dmv != NULL" failed in dm_dev_insert at
      /usr/src/sys/dev/disk/dm/dm_dev.c:195`
     backtrace: dm_dev_insert ← dm_dev_create ← dm_dev_create_ioctl ←
     dmioctl ← dev_dioctl. Guest wedged in DDB (impact = panic/DoS).
2. **Fix validation** (build.log, fix_run.log): dm.ko rebuilt in-guest with
   fix.diff (`make` in /usr/src/sys/dev/disk/dm). With the patched module:
   512 creates succeed; the 513th is **refused cleanly** (`513th create
   refused: 1`), `uptime` shows the guest alive, `dmsetup remove_all`
   succeeds. Bad behavior gone.

## Root cause chain (path:line)
- dm_dev.c:234 — `devfs_clone_bitmap_get(&dm_minor_bitmap, 0)`: the second
  argument is the *limit*; 0 means "no limit" (devfs_helper.c:221-236 only
  refuses when `limit > 0 && unit > limit`). The bitmap extends without
  bound (devfs_helper.c:78-92), so unit 512+ is handed out.
- diskslice.h:248-258 — `dkmakeminor` encodes unit bits 0-8 only
  (`(unit&0x1f)<<3 | (unit&0x1e0)<<16`); `DKMAXUNITS` is 512
  (diskslice.h:107). Unit 512 encodes identically to unit 0.
- subr_disk.c:700 + dm_dev.c:252,261 — the cdev is created with
  `dkmakewholedisk(unit)` and `dmv->minor = minor(devt)`.
- dm_dev.c:191-195 — insert collision check `_dm_dev_lookup(name,NULL,minor)`
  matches the live device by MINOR; else-branch KKASSERTs on `dmv != NULL`
  (uuid lookup was skipped because the new uuid is zero — dm_dev.c:187).
- dm_dev.c:285,290 — on destroy of a unit≥512 device, `dkunit(dmv->devt)`
  (diskslice.h:318-324) truncates to 9 bits, so
  `devfs_clone_bitmap_put` frees the ALIASED LIVE device's unit bit
  (double-allocation feeder).

## Impact / threat
- Precondition: open `/dev/mapper/control` (0640 root:operator — the same
  privilege bar as the filed dm family, DF-2442/2443/2444).
- Deterministic local kernel panic (DoS) — no race, no concurrency: 513
  sequential create ioctls (observed in <10 s of dmsetup looping; the
  512-device setup phase takes a few minutes).
- On builds without KKASSERT, or if the assert were replaced by EEXIST
  handling, the same allocation overflow yields duplicate live device
  numbers: `dm_dev_lookup_minor` (dm_dev.c:124-135) returns the first TAILQ
  match, so by-minor open-tracking (`dmopen`/`dmclose` set `is_open` on the
  wrong device, device-mapper.c:205-239) and by-minor ioctls
  (remove/status/suspend/resume/table) operate on the wrong device —
  wrong-device teardown/data-path reconfiguration, plus clone-bit
  double-allocation via the dkunit truncation.

## Relation to known findings (not a re-report)
- DF-2442 (known) is the SAME assert site but a DIFFERENT trigger: a
  concurrent NAME collision between two racing creates. Here the trigger is
  deterministic unit-space exhaustion; a single-threaded operator hits it,
  and the fix is in the allocator bound, not in insert's collision
  handling. The known list was honored: DF-1959/2442/2443/2444/2445 shapes
  were not re-derived.

## Fix
fix.diff (validated): bound allocation to the encodable unit space and
return ENOSPC with rollback; mask the put value defensively.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
