# DF-0306 — UAF in add_bw_upcall: mfc pointer used after mroute_token released

## Verdict: REAL (source-trace confirmed) — root-only path, not unpriv-escalatable

## Mechanism

`add_bw_upcall()` in `sys/net/ip_mroute/ip_mroute.c:2285-2326`:

```
2285: lwkt_gettoken(&mroute_token);
2286: mfc = mfc_find(src, dst);          // captures mfc pointer
...
2301: lwkt_reltoken(&mroute_token);       // ← releases token
2304: x = kmalloc(sizeof(*x), M_BWMETER, M_INTWAIT);  // can block
...
2319: lwkt_gettoken(&mroute_token);       // re-acquires token
2320: x->bm_mfc = mfc;                   // ← stale mfc write
2321: x->bm_mfc_next = mfc->mfc_bw_meter; // ← stale mfc deref
2322: mfc->mfc_bw_meter = x;             // ← stale mfc write
```

During the token-released gap (lines 2301→2319), a concurrent
`del_mfc()` or `MRT_DONE` can free `mfc`. The subsequent writes
through the stale `mfc` pointer are a **use-after-free** (write to
freed heap + dangling pointer installation).

## Privilege requirement

The multicast routing API (`MRT_INIT` and all subsequent `MRT_*`
setsockopts) requires a raw IGMP socket, which requires
`SYSCAP_NONET_RAW` (root). See:
- `sys/netinet/raw_ip.c:473` — `caps_priv_check(ai->p_ucred, SYSCAP_NONET_RAW)`
- `sys/net/ip_mroute/ip_mroute.c:371` — `if (so != ip_mrouter && sopt->sopt_name != MRT_INIT) return EPERM;`
- `sys/conf/files:net/ip_mroute/ip_mroute.c optional mrouting` — not even compiled into GENERIC

This is a **valid hard blocker for unprivileged escalation**: the
write is reachable only from an already-root context (a multicast
routing daemon). root→kernel is game-over by definition.

## Reproduction status

- Source trace: **confirmed** — the UAF pattern is unambiguous.
- The code is **not compiled** into the default GENERIC kernel
  (requires `options MROUTING`).
- Even with MROUTING, the race window is the `M_INTWAIT` kmalloc
  duration (microseconds unless under memory pressure), making
  reliable triggering very difficult without memory-pressure grooming.
- Not testable from an unprivileged user under any condition.

## Fix

`fix.diff` — hold `mroute_token` continuously across the `kmalloc`,
matching the existing pattern at `ip_mroute.c:1055` (add_mfc does the
same: gettoken → kmalloc M_INTWAIT → reltoken). Also switch to
`M_NULLOK` so failure is handled gracefully.

## Impact

root→kernel UAF write. In a deployment where `mrouted`/`pimd` runs
as root, a local attacker controlling the mrouter daemon (or racing
it from root) could corrupt kernel heap. Realistic impact ceiling:
root-operated daemon compromise → kernel privilege escalation beyond
the daemon's sandbox. Low practical risk since root→kernel is already
high-impact.
