# DF-3546 — VERDICT

**status: reproduced / impact: panic / confidence: certain**

## What was verified, step by step

Guest: DragonFly 6.5-DEVELOPMENT #0 (X86_64_GENERIC, stock INVARIANTS,
2026-07-02 build), QEMU/KVM single-tenant, fresh `vm.sh reset with-src`
before each decisive run.

### Baseline (stock kernel + stock /boot/kernel modules)

1. `kldload netgraph ng_socket ng_bpf` (v1 modules).
2. `./ng_cycle_v1 self` (root):
   * graph built: socket node `bpcyc` — `in` ↔ B1.x2; B1.x1 ↔ B2.y1;
     SET_PROGRAM B1: x2→x1, x1→x1; B2: y1→y1 (all accepted, stock has
     no thisHook guard).
   * 1-byte `sendto` on the data socket never returns — the ssh session
     dies with the guest. Serial console (console_self.txt, panic.txt):

         DOUBLE FAULT - KERNEL STACK GUARD HIT!
         Fatal double fault
         rip = 0xffffffff82656970    <- ng_bpf.ko base 0xffffffff82656000
         rsp = 0xfffff8008d1f6000
         panic: double fault

     rip is inside ng_bpf.ko (+0x970 = ng_bpf_rcvdata); rsp sits exactly
     at the kstack guard boundary. The recursion is:
     `ngd_send` → `NG_SEND_DATA` → `ng_send_data` (ng_base.c:1687 calls
     `(*rcvdata)(hook->peer, m, meta)` directly, same thread) →
     `ng_bpf_rcvdata` → `NG_SEND_DATA` → … with no bound anywhere in v1.
3. `vm.sh reset with-src`, then `./ng_cycle_v1 cross` (root): identical
     DOUBLE FAULT at the identical rip after a clean boot
     (console_cross_tail.txt, console_full.txt) — the no-self-reference
     3-hook cycle class panics too. (First cross attempt failed with
     ENOENT from a path bug in my trigger — `con.path` must be relative
     to the addressed node ("p"), not socket-relative ("in.p"); fixed.)

### Unprivileged reachability

uid 1001 running the same trigger: `socket(AF_NETGRAPH, …, NG_CONTROL)`
fails with errno 43 (EPROTONOSUPPORT) — v1 `ngc_attach` gates socket
creation on `caps_priv_check(SYSCAP_RESTRICTEDROOT|__SYSCAP_NULLCRED)`
(sys/netgraph/socket/ng_socket.c:172-174). The trigger is root-only;
severity capped at Low (privileged DoS), consistent with DF-3540.

### Fix validation (fix.diff, applied to the guest's /usr/src copy only)

* Part 1 — `ng_bpf_setprog()` rejects non-empty `ifMatch`/`ifNotMatch`
  equal to `thisHook` (forwarding out the arrival hook is always a
  cycle; the empty default program still passes).
* Part 2 — `ng_bpf_rcvdata()` bounds nested synchronous forwarding at 16
  hops with an atomic depth counter decremented after the forward (so
  pure cycles stay nested and trip the bound; unwinding is clean).

Module rebuilt in-guest with `-Werror` (fix_build.log, clean), loaded
from /usr/src/sys/netgraph/bpf/ng_bpf.ko:

| run | stock | patched |
|---|---|---|
| self | DOUBLE FAULT panic, guest down | `SET_PROGRAM in(x1->x1) FAILED errno=22`, guest up (fix1_self.log) |
| cross | DOUBLE FAULT panic, guest down | `sendto errno=62 (ELOOP)` — dropped at depth 16 — `RESULT no-panic survived 30s`, guest up (fix2_cross.log) |

fix_status: fixed (baseline reproduced both modes; patched reproduces
neither; per-hop filter/forward behavior intact — the ELOOP run
exercised 16 full match+forward hops).

## Why this is a real v1 finding and not a DF-3540 duplicate

DF-3540 was filed against sys/netgraph7/bpf/ng_bpf.c (itemized ng7 core,
per-node tokens → token-array KKASSERT overflow). This finding is the v1
twin: sys/netgraph/bpf/ng_bpf.c on the v1 core (sys/netgraph/netgraph/),
which has no items, no tokens and no hop accounting — the failure mode is
plain kstack exhaustion (guard-page double fault), verified on this
guest's stock v1 modules.

## Exploit ceiling

Write-capable? No — this is pure recursion depth (no memory overwrite
before the guard page). The guard-page double fault makes it a clean
privileged DoS; there is no path to uid=0 (and the trigger itself
requires root).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
