# DF-0362 — pfr_fix_anchor size_t wraparound (root→kernel memory corruption)

## Finding
`pfr_fix_anchor()` (`sys/net/pf/pf_table.c:1740-1755`) strips leading
`/` from `pfrt_anchor` with a counting loop that has no bound against
`siz=MAXPATHLEN(1024)`. Because `pfrt_name[32]` is laid out *immediately
after* `pfrt_anchor[1024]` in `struct pfr_table` (`pfvar.h:1036-1041`),
a caller that fills all 1024 bytes of `pfrt_anchor` with `/` and sets
`pfrt_name[0]='/'` makes the loop read one byte past the array; `off`
becomes 1025, so `bcopy(path, anchor, siz - off)` computes
`1024 - 1025` as a `size_t`, wrapping to `~SIZE_MAX`. The resulting
bcopy page-faults and the kernel panics.

## Reachability (verified on guest) — root-only
On DragonFlyBSD master DEV, **PF is NOT compiled into `X86_64_GENERIC`**;
it is a KLD module (`sys/net/pf/Makefile` is `KMOD=pf`). The device
`/dev/pf` does not exist on a default boot, only after `kldload pf`
(root-only). Even once loaded, `pf_load()` creates the node as
`make_dev(&pf_ops, 0, UID_ROOT, GID_WHEEL, 0600, PF_NAME)`
(`pf_ioctl.c:3360`) — i.e. **mode 0600 root:wheel**, only root can open
it. `pfsync` does not call any `pfr_*` table routine (`if_pfsync.c`
has zero references), so there is **no network-reachable path**.

Therefore the bug is a **root→kernel hardening gap** (root can panic /
corrupt its own kernel via a malformed ioctl), not an unprivileged→root
escalation. Per the audit's bright-line rule (root-only reachability is a
valid hard blocker), `uid=0` is **not** claimable here: the corruption
primitive is real, but the privilege boundary that an unprivileged
attacker would have to cross does not exist.

The realistic impact ceiling is: (a) kernel panic (DoS) from any context
that holds a readable `/dev/pf` fd — most importantly a **jailed root**
on hosts that expose `/dev/pf` into the jail for firewall management
(common practice), enabling jail→host kernel DoS; (b) latent memory-
corruption primitive if a future refactor moves PF into the kernel
proper or loosens `/dev/pf` permissions.

## Build & run
```
cc -o poc poc.c          # uses guest's /usr/include/net/pf/pfvar.h
sudo kldload pf          # create /dev/pf (root-only setup; one-time)
./poc                    # needs O_RDONLY on /dev/pf => root
```

## Expected output
- **Bug present (unpatched):** kernel panic, fatal trap 12 (page fault)
  in `bcopy`/`pfr_fix_anchor`, captured in `dfbsd-qemu/boot.log`.
- **Bug fixed (patched kernel):** ioctl returns cleanly (no panic),
  guest stays up.

## Files
- `poc.c`         — minimal trigger (corrected: ioctl #63, real headers).
- `build.sh`      — `cc -o poc poc.c`.
- `run.sh`        — `kldload pf && ./poc` (root).
- `VERDICT.md`    — full analysis.
- `fix.diff`      — `git apply`-able fix (bound loop + NUL check).
- `manifest.json` — artifact catalog.
