# DF-0428 — Verdict: NOT REPRODUCED (FALSE POSITIVE)

## One-line verdict
The claimed attack surface — `pfsync_input` reachable as a live
`IPPROTO_PFSYNC(240)` handler from any on-link host — **does not exist on
the default kernel**, on the default kernel + `pf.ko` loaded, or on the
default kernel + `pf.ko` + `pfsync0` configured. `pfsync_input` is **dead
code**. The finding mis-read the `#ifdef NPFSYNC` gate in `in_proto.c`.

## The claimed mechanism (what the finding asserted)
- `pfsync_input` (`sys/net/pf/if_pfsync.c:462-541`) is registered as the
  `IPPROTO_PFSYNC(240)` input handler via `in_proto.c:281-288`.
- It validates only sync-ifp, packet-ifp, TTL==255, version, action —
  never the source address against the configured peer.
- Any on-link host can inject `INS/UPD/DEL/CLR/UPD_C/DEL_C/UREQ/BUS`
  packets to manipulate the pf state table.

## Why it is a false positive (root cause of the reviewer's error)

The reviewer quoted `in_proto.c:281-288` but **missed the
`#ifdef NPFSYNC` ... `#endif` block** that wraps the entire registration:

```c
sys/netinet/in_proto.c:281:#ifdef NPFSYNC
sys/netinet/in_proto.c:282:    {
sys/netinet/in_proto.c:283:	.pr_type = SOCK_RAW,
...
sys/netinet/in_proto.c:288:	.pr_input = pfsync_input,
...
sys/netinet/in_proto.c:296:#endif	/* NPFSYNC */
```

**`NPFSYNC` is never defined anywhere in the source tree.** Verified by
exhaustive grep:

```
$ grep -rn NPFSYNC sys/ sys/conf/ sys/config/ sys/modules/
sys/netinet/in_proto.c:281:#ifdef NPFSYNC          ← the gate
sys/netinet/in_proto.c:296:#endif	/* NPFSYNC */  ← the gate
```

No `#define NPFSYNC`, no `options NPFSYNC` in `sys/conf/options`, no
`option/device NPFSYNC` in any kernel config (`sys/config/X86_64_GENERIC`,
`GENERIC`, `LINT`, `NOTES`, `sys/i386/conf/`, `sys/x86_64/conf/`). → The
`.pr_input = pfsync_input` entry in `inetsw[]` is **compiled out** of the
static kernel.

Nor does `pf.ko` dynamically register a handler: `nm /boot/kernel/pf.ko`
shows `pfsync_input` exported (symbol `T`) but **no reference to `inetsw`,
`protosw`, or any registration call**. `pfsyncattach` (the documented
attach hook) is also unreferenced — nothing calls it.

## Definitive evidence (five independent confirmations)

### 1. Source-level: gate is compiled out
`sys/netinet/in_proto.c:281` — `#ifdef NPFSYNC`; `NPFSYNC` undefined everywhere.

### 2. Static kernel has no `pfsync_input`
```
$ nm /boot/kernel/kernel.debug | grep -w pfsync_input
(empty)                  ← ABSENT from static kernel
```

### 3. `inetsw[]` table has NO protocol-240 entry (gdb on kernel.debug)
```
sizeof protosw = 104
inetsw entries = 13
inetsw[ 0]: pr_protocol=0    pr_input=(nil)               pr_type=0   (IP base)
inetsw[ 1]: pr_protocol=17   pr_input=0xffffffff807ccc20  pr_type=2   (UDP)
inetsw[ 2]: pr_protocol=6    pr_input=0xffffffff807b8e40  pr_type=1   (TCP)
inetsw[ 3]: pr_protocol=255  pr_input=0xffffffff807b5e40  pr_type=3   (RAW)
inetsw[ 4]: pr_protocol=1    pr_input=0xffffffff807ae0a0  pr_type=3   (ICMP)
inetsw[ 5]: pr_protocol=2    pr_input=0xffffffff8079b740  pr_type=3   (IGMP)
inetsw[ 6]: pr_protocol=46   pr_input=0xffffffff807b1de0  pr_type=3   (RSVP)
inetsw[ 7]: pr_protocol=4    pr_input=0xffffffff807a9e70  pr_type=3   (IPinIP)
inetsw[ 8]: pr_protocol=55   pr_input=0xffffffff807a9e70  pr_type=3   (MH)
inetsw[ 9]: pr_protocol=47   pr_input=0xffffffff807a9e70  pr_type=3   (GRE)
inetsw[10]: pr_protocol=41   pr_input=0xffffffff807a9e70  pr_type=3   (IPv6encap)
inetsw[11]: pr_protocol=0    pr_input=0xffffffff807b5e40  pr_type=3   (RAW wildcard)
inetsw[12]: pr_protocol=112  pr_input=0xffffffff807a35e0  pr_type=3   (VRRP/CARP)
```
**240 (IPPROTO_PFSYNC) is absent.**

### 4. Dispatch path: `ip_protox[240]` resolves to the RAW wildcard
`ip_init` (`sys/netinet/ip_input.c:343-351`) first sets every
`ip_protox[i]` to the IPPROTO_RAW wildcard index, then overwrites only
the protocols that actually appear in `inetsw[]`. With no protosw
carrying `pr_protocol=240`, `ip_protox[240]` remains the wildcard →
`transport_processing_oncpu` (`ip_input.c:409-415`) dispatches any
`ip_p=240` packet to `rip_input`, **never to `pfsync_input`**:
```c
sys/netinet/ip_input.c:409:	const struct protosw *pr = &inetsw[ip_protox[ip->ip_p]];
sys/netinet/ip_input.c:415:	pr->pr_input(&m, &hlen, ip->ip_p);
```

### 5. Live guest: handler is not registered, injection does nothing
On `6.5-DEVELOPMENT #0` with `pf.ko` loaded and `pfctl -e`'d:
- `netstat -s` enumerates registered IP protocols: `tcp, udp, ip, icmp,
  igmp, carp, ip6, icmp6`. **No pfsync** — no handler registered.
- Injecting a crafted proto-240 CLR packet (`inject_pfsync 10.0.2.99
  224.0.0.240 0xdeadbeef`, TTL=255) from a raw socket produces **no
  kernel message, no pf-state change, no observable pfsync activity**.

The comment the finding claimed was stale — *"This function is not yet
called from anywhere"* (`sys/net/pf/if_pfsync.c:486`) — is in fact
**accurate**.

## Impact of this false positive
None. The "any on-link host can inject pfsync packets" primitive the
finding describes is not reachable. `pfsync_input` is dormant code;
`pf.ko` only ever *sends* pfsync packets (via `pfsync_sendout`), it does
not *receive* them on this platform.

This is consistent with DragonFlyBSD history: the in-kernel pfsync
*receiver* was never finished/wired up (hence the `#ifdef NPFSYNC` guard
and the honest "not yet called" comment). The sender side is functional
(`pfsync_sendout_mbuf`, multicast to 224.0.0.240), but no peer host
running this same kernel will process the messages.

## Side observation (NOT DF-0428 — separate latent bug)
While configuring `pfsync0` with a syncdev via `SIOCSETPFSYNC` to test the
input path, the guest hit a **different** panic in `pfsync_bulkfail`:

```
panic: assertion "LWKT_TOKEN_HELD_ANY(&pf_token)" failed in pfsync_bulkfail at if_pfsync.c:1621
pfsync_bulkfail() at pfsync_bulkfail+0x71
softclock_handler() at softclock_handler+0x1b8
```

`pfsync_bulkfail` (`sys/net/pf/if_pfsync.c:1617`) asserts the pf_token is
held, but it is invoked from a callout (`softclock_handler`) without the
token. This is a **root-only local DoS** (requires `SIOCSETPFSYNC` privilege)
triggered ~5 s after configuring a syncdev. It is unrelated to DF-0428's
network-injection claim and is not the subject of this verification. Filed
here as a side note for the team's awareness; it deserves its own finding
if not already tracked.

## Build / run (reproduce the negative result)
```
./build.sh                  # builds setup_pfsync + inject_pfsync
# (root on guest): kldload pf.ko; pfctl -e
./run.sh                    # injects a proto-240 CLR packet
# expected on master DEV: packet sent, NO kernel response, NO pfsync
# protocol in `netstat -s`, NO pf-state change  ->  handler unreachable
```

## PoC changes
- `setup_pfsync.c` — NEW. Issues `SIOCSETPFSYNC` directly (DragonFly's
  `ifconfig` does not expose `syncdev`/`syncpeer` for pfsync). Used to
  configure `pfsync0` with a syncdev + peer + maxupdates.
- `inject_pfsync.c` — NEW. Crafts a raw IP packet (`ip_p=240`,
  `ip_ttl=255`) carrying a `PFSYNC_ACT_CLR` payload with an arbitrary
  spoofed source IP and a chosen creatorid, sent via `IPPROTO_RAW`.
  This is the "on-link attacker" the finding describes.
- No `exploit.c` — there is no memory-corruption primitive (the finding
  is an auth-bypass claim), and the auth-bypass surface itself is dead.

## Recommended fix
**No code change needed: false-positive.** The cited handler
(`pfsync_input`) is unreachable on the default kernel and on
`pf.ko`-loaded kernels because the `#ifdef NPFSYNC` registration block
in `in_proto.c:281-296` is compiled out (`NPFSYNC` is never defined) and
`pf.ko` does not dynamically register a handler.

If the project ever intends to wire up the pfsync *receiver* (define
`NPFSYNC`, finish the integration), then the missing-source-authentication
concern the finding raises becomes a real hardening item: at minimum,
compare `ip->ip_src` against `sc->sc_sync_peer` when a unicast peer is
configured, and require a non-multicast peer. But that work is gated on
first making the code reachable, which is not the case today.
