# DF-0689 — encaptab list UAF race (sys/netinet/ip_encap.c)

## Status: NOT REPRODUCED (root-only writer side)

The race is **structurally real** but cannot be triggered by an
**unprivileged local user**:

- Reader (encap4_input / encap6_input): walks `encaptab` lockless
  (ip_encap.c:174, :268). Reachable by anyone who can send IP/IPv6 to
  the host (so remotely reachable in principle).
- Writer (encap_detach): requires root-only `SIOCDIFPHYADDR`
  (sys/net/if.c:2343 `caps_priv_check(cred, SYSCAP_RESTRICTEDROOT)`).
- An **unprivileged local user cannot trigger the writer side** at all,
  so they cannot open the race window. The race requires concurrent
  privileged admin (tearing down a gif/stf/gre) while a remote party
  floods the tunnel.

Per the audit's realistic-threat test, root→kernel races are game-over
(root can already do anything). The bug is a hardening gap, not an
unprivileged escalation. We document and provide a fix.diff.

## How to (try to) reproduce as root

```
kldload if_gif.ko
ifconfig gif0 create
ifconfig gif0 tunnel 127.0.0.1 127.0.0.2
# Flooder (separate host or loopback):
#   ping -f 127.0.0.1   # to keep packets flowing into encap4_input
# Concurrently as root, repeatedly:
ifconfig gif0 destroy    # triggers encap_detach during reader walk
```

The race window is narrow; even on SMP it may need many iterations.

## Fix

`fix.diff` adds a global `lwkt_token` held across the encaptab walk and
dispatch in encap4_input / encap6_input, and across the LIST_INSERT_HEAD
/ LIST_REMOVE in encap_add / encap_detach. This makes the reader/writer
mutually exclusive without changing the data structure.
