{
  "finding_id": "DF-3427",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED at runtime with the exact predicted crash signature, plus one mechanism refinement that widens the blast radius. generic_set_tx_event (armed on ring exhaustion, netmap_generic.c:567-574, and on dispatch failure :538) installs SET_MBUF_DESTRUCTOR (ext_free = generic_mbuf_destructor) and m_freem()s the pool entry. Runtime shows the armed mbufs own free does NOT invoke ext_free (m_frees combined-cache branch, sys/kern/uipc_mbuf.c:1371-1407, caches mbuf+cluster without calling ext_free), so the stale ext_free is a landmine in a freed object: objcache_put runs no destructor and objcache_gets magazine fast path (kern_objcache.c:493-517) does not re-run the cache ctor, so the booby-trapped mbuf is handed to the next allocator as-is. In the decisive run a live UDP spray mbuf inherited the destructor; when udp_input()s m_freem/m_free took an ext_free-invoking branch (uipc_mbuf.c:1412/1433), generic_mbuf_destructor was called with m = ext_arg = a 16-byte struct mbcluster, read m_pkthdr.rcvif at offset 40 (OOB), and dereferenced the garbage pointer as ifp->if_capenable: Fatal trap 12: page fault, fault address 0x76 / 0xffffffff00000076, Stopped at generic_mbuf_destructor+0x3f: testb $0x80,0x75(%rdi), guest dead in DDB. The findings secondary claims (permanent cluster leak, mclmeta_dtor INVARIANTS panic at uipc_mbuf.c:632) were NOT observed: m_mclfree shadowing never happens because the pool mbufs free path never calls ext_free. Gating identical to DF-3426 (/dev/netmap 0660 root:wheel).",
  "exploit_chain": "root/wheel -> open /dev/netmap -> NIOCREGIF tap0 -> NIOCTXSYNC until nr_hwavail==0 (ring exhaustion) -> generic_set_tx_event writes ext_free=generic_mbuf_destructor into a pool mbuf (already freed by driver TX completion in the same lap) and m_freem()s it (double free, DF-3426 family) -> the freed combined mbuf+cluster object carries the stale destructor through objcache_put/get (magazine fast path, no ctor re-run) -> any subsystems allocation inherits it (demonstrated: UDP spray) -> that mbufs normal free calls ext_free(ext_arg=mbcluster) -> generic_mbuf_destructor reads m_pkthdr.rcvif 32+ bytes past the 16-byte mbcluster -> garbage ifp -> netmap_generic_irq derefs ifp->if_capenable -> kernel page fault / panic. Cross-subsystem deferred detonation; attacker-controlled heap grooming determines the OOB-read garbage (here 0x1 / 0xffffffff00000001 -> non-canonical deref).",
  "evidence": "run.log + panic_uaf_run2_serial.txt (decisive: RIG: generic_mbuf_destructor CALLED m=0xfffff8008d402cc0 with the m_free<-m_free<-m_freem<-udp_input<-ip_input<-netmsg_service_loop backtrace, then Fatal trap 12 at generic_mbuf_destructor+0x3f testb $0x80,0x75(%rdi), fault 0xffffffff00000076); panic_uaf_run1_serial.txt in ../DF-3426 (first reproduction, fault 0x76); run_evt.log (arming in isolation: generic_set_tx_event e=511 on an already-freed slot, destructor dormant, guest survives); fix_run.log = fix_validation27.log (combined fix: 0 destructor callbacks, all PoC modes rc=0); VERDICT.md (full mechanism)",
  "kernel_refs": [
    "sys/net/netmap/netmap_generic.c:101",
    "sys/net/netmap/netmap_generic.c:322",
    "sys/net/netmap/netmap_generic.c:327",
    "sys/net/netmap/netmap_generic.c:331",
    "sys/net/netmap/netmap_generic.c:431",
    "sys/net/netmap/netmap_generic.c:439",
    "sys/net/netmap/netmap_generic.c:443",
    "sys/net/netmap/netmap_generic.c:567",
    "sys/kern/uipc_mbuf.c:1371",
    "sys/kern/uipc_mbuf.c:1412",
    "sys/kern/uipc_mbuf.c:1433",
    "sys/kern/uipc_mbuf.c:652",
    "sys/kern/kern_objcache.c:493"
  ],
  "poc_changes": "Shared rig with DF-3426 (verbatim production module + WNA side-table shim + a_fpp port shim + RIG_TRACE; see that pack). Trigger rewritten from the documentation stub into the real userspace rig; RIG_TRACE adds a print_backtrace(-6) at generic_mbuf_destructor entry which captured the udp_input detonation path.",
  "attempts": 3,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 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": 3060,
  "guest_dirty": 0,
  "build_cmd": "same rig as DF-3426: ./build.sh (netmap-src/netmap.ko + ./trigger); full log build.log",
  "run_cmd": "./run.sh uaf 512 4096 16384   # destructor detonation -> Fatal trap 12; ./run.sh evt   # arming in isolation",
  "code_hash": "c7fa707cf9661b7ef8de1b077cf35bb6e6fda102a11a7ef5f2c7dcce1aec7406",
  "notes": "Mechanism correction vs. the filing (see VERDICT.md): the armed mbufs own free never invokes ext_free on this port (CLCACHE+sharecount==1 branch caches the pair silently); the detonation is deferred via objcache magazine recycling into an arbitrary subsystems live mbuf (udp_input demonstrated). The findings secondary leak/mclmeta_dtor claims were not observed. One incidental note: the objcache magazine fast path not re-running ctors amplifies any stale per-object callback state and merits standalone hardening review. Incidental code observation (one line, no action): generic_netmap_tx_clean()s while (ntc != hwcur) replenish loop can livelock allocating mbufs under memory pressure (netmap_generic.c:356-376) - out of scope here.",
  "recommended_fix": "Make SET_MBUF_DESTRUCTOR a no-op on DragonFly (ext_free receives ext_arg = mbcluster, ABI-incompatible with the mbuf-typed destructor) and keep generic_mbuf_destructor __unused for documentation; TX completion is already reported by generic_netmap_tx_clean() polling. See fix.diff (validated).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff (SET_MBUF_DESTRUCTOR no-op + generic_mbuf_destructor marked __unused; validated as the combined fix26+fix27 module) applied and rebuilt: the identical PoC matrix (uaf 512 4096 16384, evt, unreg 8) runs rc=0 with netstat -m exactly flat and dmesg shows ZERO generic_mbuf_destructor callbacks (fix_run.log = fix_validation27.log, fix_build.log). The macro can no longer plant ext_free landmines in recycled mbufs. fix.diff applies cleanly to the pristine sys tree (patch -p4 --dry-run). Note: DF-3426s slot-clearing fix alone also prevents this arming (event slot always NULL); this fix is the defense-in-depth that kills the ABI-incoherent primitive itself.",
  "fix_evidence": "fix_run.log (= fix_validation27.log: combined-fix PoC matrix, 0 destructor calls, flat counters, uptime OK); fix_build.log (fixed-module build); fix.diff (tree patch, macro no-op + __unused); fix2627-rig.diff (rig variant)"
}