# DF-3297 — pf DIOCGETSRCNODES leaks kernel heap pointers (RB-tree links)

## What
`pfioctl()` DIOCGETSRCNODES copies each `struct pf_src_node` to userland
with a raw `bcopy(n, pstore, sizeof(*pstore))` (pf_ioctl.c:2884) followed by
`copyout` (pf_ioctl.c:2902).  The only sanitization is a 4-byte store
`pstore->rule.nr = n->rule.ptr->nr` (pf_ioctl.c:2886) into the 8-byte
`union pf_rule_ptr`.  Leaked verbatim:

- `entry.rbe_left` / `entry.rbe_right` / `entry.rbe_parent` — raw RB-tree
  kernel heap pointers (non-NULL whenever a per-cpu src tree holds >=2
  nodes)
- the upper 4 bytes of `rule.ptr` (the union's high half survives the
  u32 `nr` store)
- `kif` (always NULL today — `pf_insert_src_node`, pf.c:582-599, never
  sets it — but is copied raw)

Sibling of the DF-0277 (DIOCGETRULE) / DF-0278 (DIOCGETADDR) /
DF-0279 (DIOCGETALTQ) leak family — different ioctl, not covered by
those findings.  Unlike those, DIOCGETSRCNODES is in the FREAD allow
list (pf_ioctl.c:1069), so a **read-only** /dev/pf fd suffices wherever
one is granted.

## Reachability
/dev/pf is `0600 root:wheel` on a stock system (pf_ioctl.c:3360), so on
stock this is a root-visible pointer disclosure (KASLR-defeat class).
Setups that relax devfs permissions for monitoring (pfctl -s Sources
uses this ioctl) expose it to non-root readers.

## Reproduce
```
kldload pf.ko
pfctl -f srcnodes.conf && pfctl -e
for i in 2 3 4 5 6 7 8; do ifconfig lo0 127.0.0.$i netmask 255.0.0.0 alias; done
for ip in 127.0.0.2 ... 127.0.0.8; do ./conn2 $ip & done; wait   # >=2 nodes/cpu
cc -O2 -o leak_srcnodes leak_srcnodes.c
./leak_srcnodes        # opened O_RDONLY
```

## Expected
```
node 1:
  +0000: 0xfffff8008bb64460   <-- kernel pointer   (rbe_left)
  +0008: 0xfffff8008bb645a0   <-- kernel pointer   (rbe_right)
node 0:
  +0010: 0xfffff8008bb64500   <-- kernel pointer   (rbe_parent)
```
(genuine DFly kernel-heap addresses 0xfffff8xx_xxxxxxxx; stable across
consecutive dumps — see leak_sample.txt)
