{
  "finding_id": "DF-3362",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Reproduced deterministically: with `scrub in on tap0 all fragment crop`, two tap-injected IPv4 fragments (first fills cache range [0,8000), second overlaps at off=496 with 8000B payload, arriving as the ~35-mbuf MGET chain tapwrite() always builds) hit pf_fragcaches precut>0 chop path: m_dup() of the multi-mbuf chain, tail-trim via m_adj(dup, negative) which zeroes but does NOT unlink trailing mbufs, then KASSERT((*m0)->m_next == NULL) at pf_norm.c:667 fires -> panic: (*m0)->m_next != NULL: pf_fragcache with backtrace pf_fragcache -> pf_normalize_ip -> pf_test -> pf_check_in -> pfil_run_hooks. Guest died in DDB; hard reset required. The twin KASSERT at :684 rests on the same single-mbuf assumption. INVARIANTS/debug kernels only: production non-INVARIANTS kernels compile the KASSERTs out and continue with zero-length mbufs chained (benign for the packet, violates the invariants). Fix validated in-guest: single-fix kernel (#1, 2026-09-11) with fix.diff (NULL-check reorder + unlink zero-length tail + assert on m_pkthdr.len) \u2014 same trigger no longer panics, regression controls (max-mss path, reassemble path) pass. The reorder also closes known DF-0391s m_dup NULL deref in the same hunk.",
  "exploit_chain": "",
  "evidence": [
    "findings/poc/DF-3362/panic.txt (serial console: panic string + backtrace)",
    "findings/poc/DF-3362/fragcache_kassert.c (two-frame tap injector)",
    "findings/poc/DF-3362/fix_validation.md (baseline panic vs patched no-panic, same trigger)",
    "sys/kern/uipc_mbuf.c:1859 (m_adj tail-trim zeroes trailing mbufs without unlinking)",
    "sys/net/tap/if_tap.c tapwrite (MGETHDR+MGET chain input path)"
  ],
  "kernel_refs": [
    "sys/net/pf/pf_norm.c:661-669",
    "sys/net/pf/pf_norm.c:670-687",
    "sys/kern/uipc_mbuf.c:1859-1917",
    "sys/net/tap/if_tap.c:927-985"
  ],
  "poc_changes": "Seed rewritten: lo0 raw-socket injection cannot carry >16KB multi-mbuf fragments (lo0 MTU cap 16384, TAPMRU-style limits, local-source validation); switched to /dev/tap0 Ethernet-frame injection which reproduces the multi-mbuf input shape exactly (tapwrite builds MGETHDR+MGET chains).",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0/#1: stock X86_64_GENERIC INVARIANTS kernel for the panic; #1 Fri Sep 11 10:20:27 UTC 2026 single-fix kernel for validation",
  "runtime_sec": 900,
  "guest_dirty": 0,
  "build_cmd": "cc -O2 -o fragcache_kassert fragcache_kassert.c  # fix build: cd /usr/src && patch -p1 < fix.diff && make nativekernel && make installkernel",
  "run_cmd": "pfctl -f fragcrop.conf && pfctl -e && /root/poc/fragcache_kassert",
  "code_hash": "01b01b542be7073404bee9a5281b907eef64498827324bc1af60275d872f9081",
  "notes": "Severity Low: remote-deterministic two-packet panic but gated on INVARIANTS kernels + fragcrop scrub mode. Non-INVARIANTS kernels proceed unharmed. Guest was hard-reset (vm.sh reset with-src) after the baseline panic and will be reset again after this run.",
  "recommended_fix": "See fix.diff: reorder m_dup NULL check before m_adj, unlink zero-length trailing mbufs after the tail-trim, and assert on m_pkthdr.len instead of m->m_len. Validated in-guest.",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #1: Fri Sep 11 10:20:27 UTC 2026 x86_64 (X86_64_GENERIC + fix.diff)",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Baseline kernel #0 panicked on the trigger (panic.txt). Single-fix kernel #1 with fix.diff: identical trigger completes with EXIT=0, no panic, guest healthy; scrub max-mss and reassemble regression controls pass. The panic is gone and the chop path works.",
  "fix_evidence": [
    "findings/poc/DF-3362/fix_validation.md",
    "findings/poc/DF-3362/fixbuild_excerpt.txt",
    "findings/poc/DF-3362/panic.txt"
  ]
}