# DF-0923 — PoC: UAF of `vmspace`/`vm_map` (and `vm_map_backing`) across the per-iteration unlock in `/proc/<pid>/map`

## Bug (one line)
`procfs_domap()` (`sys/vfs/procfs/procfs_map.c`) caches
`map = &p->p_vmspace->vm_map` **without** `vmspace_hold()`, drops the vm_map
read lock per iteration (`:142-143`), then re-locks/iterates the now-stale
cached `map` (`:230`, `:89`). A concurrent `execve()`/`exit()` on the target
frees the vmspace → use-after-free → kernel panic.

## Threat model
Unprivileged local user. `/proc/<pid>/map` is `-r--r--r--` (world-readable).
The `maxx` user (uid 1001, not in wheel) forks a child that tight-loops
`execve` (freeing/replacing its vmspace) and reads `/proc/<child>/map` in a
tight loop, racing the per-iteration lock-drop window.

## Build & run
```
cc -O2 -o race_map race_map.c
./race_map [seconds] [nreaders]      # default 30 s, 4 readers
```
Run as an unprivileged user (e.g. `maxx`). The race is statistical but, on the
unpatched kernel, panics within seconds every time. The PoC is self-contained:
the `--victim` mode re-execs itself to drive vmspace churn; the `--reader`
mode hammers `/proc/<victim>/map`.

## Expected output
- **Unpatched kernel (#0):** kernel panic within seconds (guest dies; ssh drops):
  ```
  Fatal trap 12: page fault while in kernel mode
  fault virtual address = 0x8
  Stopped at vm_map_rb_tree_RB_NEXT: movq 0x8(%rdi),%rax
  ```
- **Patched kernel (#1, fix.diff applied):** runs to completion cleanly:
  ```
  [parent] survived Ns without panic; tearing down
  [parent] done (no panic observed this run)
  ```

## Notes
- The reader uses a **4 KB** read buffer deliberately (see the comment in
  `race_map.c`): `procfs_domap` sizes its sbuf from `uio_offset + uio_resid`,
  and a large resid under many parallel readers independently trips
  `sbuf: malloc limit exceeded` — an unrelated pre-existing issue that would
  mask the UAF. 4 KB still drives `procfs_domap` through every map entry
  (every per-iteration unlock window), which is all the UAF race needs.
- Escalation to `uid=0` is **not** achievable: read-dominant UAF of a
  dedicated-slab object (`vmspace_cache`); no attacker-controlled write to a
  victim object. See `VERDICT.md` for the full Phase-6 analysis.
- The validated fix is `fix.diff` (add `vmspace_hold`/`vmspace_drop` mirroring
  `procfs_rwmem`, keep `p_token` held for LIFO token order, EFAULT checks
  before `sbuf_new` to avoid a leak).
