# DF-1843 PoC

Trigger: race `dmopen` on `/dev/mapper/race0` against
`dm_dev_remove_ioctl` on `/dev/mapper/control`. If the remove wins the
race (reads `is_open == 0` before `dmopen` sets it to 1), the `dm_dev`
is freed while the open fd's `dev->si_drv1` still points to it. Any
subsequent I/O through the dangling fd dereferences freed memory.

## Preconditions

* `device dm` compiled in.
* `/dev/mapper/control` access (mode 0640 root:operator — operator group).
* Ability to open the DM block device (devfs rules permitting).

## Race logic

1. Thread A (opener): `open("/dev/mapper/race0", O_RDWR)` — this calls
   `dmopen` which does `dm_dev_lookup` (ref_cnt++) then immediately
   `dm_dev_unbusy` (ref_cnt--) and sets `is_open = 1` with no lock.
2. Thread B (remover): issues `NETBSD_DM_IOCTL` with
   `{command: "remove", name: "race0"}` — this calls
   `dm_dev_remove_ioctl` which reads `is_open` (still 0), unbusy, and
   proceeds to `dm_dev_remove` → `disable_dev` → wait for ref_cnt==0 →
   `dm_dev_destroy` → `dm_dev_free(dmv)` → `kfree`.
3. If Thread B wins: Thread A's `dmopen` returns 0 with
   `dev->si_drv1` → freed `dmv`. Thread A's `read()` calls `dmstrategy`
   which dereferences `dmv->table_head` etc. on freed memory.

## Build

```
cc -o dm_uaf dm_uaf.c -lprop
```

(The skeleton above documents the race; the PoC runner must fill in the
proplib `NETBSD_DM_IOCTL` dictionaries for create/reload/resume/remove
using libprop.)

## Run

```
./dm_uaf     # as operator group member; let it run ~30s
```

## Expected output

```
# DoS floor:
kernel panic: freed memory dereference in dmstrategy
dmstrategy+0x.. at 0x..
dev_dstrategy+0x.. at 0x..

# Escalation (with heap grooming):
# Spray kmalloc-512 to reclaim the freed dm_dev struct with controlled
# data, then trigger I/O through the dangling fd to hijack control flow.
```

## Fix

See the finding markdown: hold the busy reference for the lifetime of
the open (`dmopen` does NOT call `dm_dev_unbusy`; `dmclose` does). This
makes `disable_dev`'s `ref_cnt == 0` wait block until `dmclose`,
preventing the UAF.
