{
 "finding_id": "DF-3298",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "leak",
 "confidence": "certain",
 "verdict": "Deterministic use-after-free in carp_proto_input(): an IP-fragmented proto-112 datagram whose first fragment was re-input on a carp(4) interface (rcvif stamped to the ifnet embedded in the carp_softc) completes reassembly AFTER ifconfig carp0 destroy; nothing purges the per-CPU reassembly queues at ifnet free, so carp_proto_input() executes on the freed chunk (if_type/if_flags/if_softc derefs; if_xname printed to console from freed memory) with vmstat -m proving 0 M_CARP allocations at that moment. The deeper attacker-controlled 8-byte write (sc->sc_counter, ip_carp.c:1150-1151) is code-reachable but blocked on the stock kernel because carp_stop() clears IFF_RUNNING in the same chunk pre-free; M_CARP zone type-isolation (only carp softcs reuse the chunk) prevents unprivileged grooming, so no uid=0 today - impact is a deterministic freed-memory read incl. stale-string disclosure, remotely triggerable by an L2-adjacent unauthenticated attacker racing ordinary admin destroy churn. fix.diff (synchronous reassembly-queue purge on all netisrs from carp_clone_destroy before kfree) authored and validated on a guest-built kernel: post-destroy fragment no longer reaches carp_proto_input (0 packets received vs 5->6 baseline), while fragmented input to a live carp interface still works.",
 "exploit_chain": "L2 attacker sends fragment#1 (offset 0, MF) to the public CARP unicast MAC 00:00:5e:00:01:vhid while carp0 is MASTER -> carp_forus claims it, ether_reinput_oncpu stamps rcvif=carp0-ifnet, ip_reass queues it (queue keyed only by id/src/dst/proto); admin runs ifconfig carp0 destroy (kfree of softc+ifnet, M_CARP in-use 0); attacker sends fragment#2 which completes reassembly on the parent NIC -> head pkthdr (dangling rcvif) survives m_cat -> carp_proto_input dereferences freed memory. Demonstrated: stale reads + console disclosure of freed-chunk if_xname. Write path (sc_counter, 8 controlled bytes) blocked by pre-free IFF_RUNNING clearing + M_CARP zone isolation (documented hard blocker, path:line in VERDICT.md).",
 "evidence": [
  "run.final.log: BEFORE 5 packets received -> destroy (M_CARP in-use: 0) -> frag2 -> 6 packets received on freed softc",
  "run.final.log + run.repeat.log: dmesg carp_proto_input: packet received on stopped carp interface: carp0 - if_xname read from freed chunk, 3/3 stress runs",
  "run.patched.log: fix-validated kernel - 0 packets received, 1 fragment dropped (purge), 0 carp_proto_input log lines",
  "run.patched.control.log: live carp0 control still receives the reassembled datagram (fix does not break legit input)",
  "VERDICT.md path:line index"
 ],
 "kernel_refs": [
  "sys/netinet/ip_carp.c:1219",
  "sys/netinet/ip_carp.c:703",
  "sys/netinet/ip_carp.c:956",
  "sys/netinet/ip_carp.c:1150",
  "sys/netinet/ip_input.c:1004",
  "sys/netinet/ip_input.c:1275",
  "sys/net/if_ethersubr.c:1317",
  "sys/netinet/in_proto.c:317"
 ],
 "poc_changes": "Seed sketch had no runnable source; wrote frag_send.c (tap-injected raw frames, variants A1/A2/B1/B2 with kernel-exact CARP HMAC), setstate.c (SIOCSVH state force, read-modify-write to preserve key), mjoin.c (group join for variant B), setup.sh/run.sh. tap(4) used instead of BPF because BPF writes transmit rather than inject-as-received; tapwrite feeds the real ether_input path.",
 "attempts": 6,
 "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
 "runtime_sec": 420,
 "guest_dirty": 0,
 "build_cmd": "cc -O2 -Wall -o frag_send frag_send.c; cc -O2 -Wall -o setstate setstate.c; cc -O2 -Wall -o mjoin mjoin.c",
 "run_cmd": "sh setup.sh && sh run.sh",
 "code_hash": "cdcaf5ef848ade6a4bdd4f99d6cbee1a9afbf011610803972a063f7f919f8fc4",
 "notes": "Known findings DF-0301..0305 were NOT re-reported. Impact mapped to leak (stale freed-memory disclosure incl. console-printed if_xname; the corruption write is blocked - see VERDICT.md Why not uid=0). Guest reset to clean source after fix validation.",
 "recommended_fix": "Purge all IP reassembly queues holding fragments with m_pkthdr.rcvif == destroyed ifnet (synchronously on every netisr) before freeing the carp softc - see fix.diff; generically this purge belongs in if_detach() for all ifnets."
}