# DF-0589 — PoC: race sc->outq IF_DEQUEUE (ng_h4_start) vs IF_DRAIN (ng_h4_disconnect)

Privileged-capability local race. The per-node mbuf output queue `sc->outq` is
mutated from two execution contexts (tty `l_start` callback vs netgraph
disconnect/reset/shutdown) that hold no common cross-CPU lock: `NG_H4_LOCK`
is just `crit_enter()` (per-CPU), and `ng_h4_start` doesn't even take that
around its `IF_DEQUEUE`/`IF_PREPEND`. On SMP, concurrent dequeue and drain can
both return the same mbuf → UAF / double-free.

## Files

- `race.c` — sketch driver (flood outq via ng data socket, race disconnect
  against tty `l_start`).
- (added by per-PoC verifier) full `race.c` rewrite with proper ng_socket /
  ngctl plumbing, `build.sh`, `run.sh`, `build.log`, `run.log`, `VERDICT.md`,
  `manifest.json`, and — if escalation is developed — `exploit.c` plus the
  mbuf-slab grooming recipe.

## Build & run

```
cc -O2 -lpthread -o race race.c
./race /dev/cuaU0          # user with SYSCAP_NONET_NETGRAPH capability, SMP
```

## Expected first outcome

Kernel panic from double-free (mbuf allocator INVARIANTS, or freed-mbuf
KASSERT), with backtrace pinning the fault inside `m_free` called from
`ng_h4_start` (sys/netgraph7/bluetooth/drivers/h4/ng_h4.c:610):

```
Kernel panic: mbuf double-free / use-after-free
    m_free+0x...
    ng_h4_start+0x...
    l_start ...
```

Or, on a non-INVARIANTS kernel, silent corruption of the mbuf slab leading to
a later panic from use-after-free in `clist_btoq`/`m_freem`.

## Notes for the per-PoC verifier

- The race window is narrow: the `outq` is only 12 mbufs deep, and tty
  draining is typically bursty not continuous. Plan for many flood+disconnect
  iterations (thousands-to-millions) on SMP hardware. Adjust `FLOOD_COUNT`
  and the inter-disconnect pause to maximize overlap with the tty
  `l_start` callback.
- The escalation variant (mbuf-slab grooming → controlled `m_ext.ext_free`
  → ring-0 code execution) requires a `sizeof(struct mbuf)` and victim-object
  analysis; document the chosen victim in `VERDICT.md`. If the heap layout
  does not admit a stable primitive, the verdict should reflect
  `DoS_confirmed / escalation_unverified` and the finding stays at Medium.
- Verify the fix with `git apply findings/poc/DF-0589/fix.diff` (upgrading
  `NG_H4_LOCK` to a spinlock and wrapping `IF_DEQUEUE`/`IF_PREPEND` in
  `ng_h4_start`); after the fix the race should no longer fire.
- The same pattern exists in `sys/netgraph7/tty/ng_tty.c` (out of this file's
  scope but flagged in the finding markdown for the maintainer).
