{
 "finding_id": "DF-3344",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "panic",
 "confidence": "certain",
 "verdict": "lagg_port_ifdetach is registered on ifnet_departure_event (if_lagg.c:218-220), an eventhandler key that nothing in the DragonFly kernel ever invokes (if_detach fires ifnet_detach_event, if.c:958); the only automatic port-removal path is therefore dead code. Destroying a member interface (e.g. ifconfig tap0 destroy frees the whole tap_softc with its embedded ifnet, if_tap.c:495) leaves a persistent dangling lp->lp_ifp in sc_ports. Demonstrated on the guest: (1) unprivileged SIOCGLAGG (any uid) keeps returning the destroyed member, reading its name from freed heap - after slab reuse the port name mutated from tap0 to tap2, i.e. a live kernel-heap-reuse disclosure to unprivileged users; (2) unprivileged UDP tx through the dangling failover primary exercised freed-memory reads in lagg_link_active (survived - stale flags read inactive); (3) privileged ifconfig lagg0 laggproto lacp drove lacp_port_create -> if_addmulti through the freed ifnet and panicked the kernel: Fatal trap 12, page fault at 0x3c0, Stopped at tapifioctl+0x158; guest down at DDB. Not a race - the UAF is permanent until the lagg is destroyed, and lagg destroy itself writes restores into the freed ifnet (if_lagg.c:763-766). Two-line fix (ifnet_detach_event) validated on a rebuilt kernel: member destroy now removes the port, the previously-fatal trigger succeeds, guest stays up.",
 "exploit_chain": "root: lagg0 create + laggport tap0/tap1 + up + IP -> root: ifconfig tap0 destroy (dangling port installed; UAF is now persistent) -> unprivileged: SIOCGLAGG loop reads freed-then-reused kernel heap through lp_ifp->if_xname (demonstrated: name flipped tap0->tap2 when objcache reused the chunk) -> unprivileged tx can dispatch netmsgs into the dangling/aliased ifnet (content-dependent; survived in this run) -> privileged admin op on the lagg (laggproto lacp demonstrated) faults through the freed ifnet -> Fatal trap 12 panic. Full uid=0 chain not developed: would require grooming the freed chunk with a chosen victim object and pivoting via lagg_port_destroys unconditional if_type/if_ioctl/if_output/if_lagg restores into the dangling ifnet (if_lagg.c:763-766); demonstrated ceiling is persistent unprivileged heap disclosure + reliable panic.",
 "evidence": [
  "findings/poc/DF-3344/run.log - steps [3]/[4]: destroyed tap0 still listed (root+unpriv), post-destroy hex name from freed heap",
  "findings/poc/DF-3344/run.log - step [6]: unprivileged dump flips port[1] tap0->tap2 after tap2 create (dangling pointer into reused chunk)",
  "findings/poc/DF-3344/panic.txt - Fatal trap 12 ... fault virtual address = 0x3c0 ... Stopped at tapifioctl+0x158 reached via laggproto lacp on dangling member",
  "findings/poc/DF-3344/fix_validation.log - patched kernel #1: ports=1 after destroy, LACP-OK/RR-OK, guest up"
 ],
 "kernel_refs": [
  "sys/net/lagg/if_lagg.c:218",
  "sys/net/lagg/if_lagg.c:223",
  "sys/net/lagg/if_lagg.c:924",
  "sys/net/lagg/if_lagg.c:2085",
  "sys/net/lagg/if_lagg.c:763",
  "sys/net/lagg/if_lagg.c:951",
  "sys/net/if.c:958",
  "sys/net/if_var.h:724",
  "sys/net/tap/if_tap.c:495",
  "sys/net/lagg/ieee8023ad_lacp.c:544"
 ],
 "poc_changes": "no seed existed (new pass-2 finding); laggleak.c/udptx.c written from scratch. ifconfig syntax corrected during bring-up: DF uses laggport/laggproto, not FreeBSD addm. SIOCGLAGG requires a preallocated port array passed via ra_port/ra_size (kernel recounts and clamps).",
 "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": 5400,
 "guest_dirty": 0,
 "build_cmd": "cc -O -o /tmp/laggleak laggleak.c -I/usr/src/sys && cc -O -o /tmp/udptx udptx.c",
 "run_cmd": "./run.sh (vm.sh run_root setup+destroy+trigger; vm.sh run_user leak/tx as uid=1001)",
 "code_hash": "b33ffb1cbfe9772c3e753803a0fa81627cdc7de74a6c95b0f7758f8370ae0686",
 "notes": "runtime includes the in-guest kernel rebuild for fix validation (~35 min). Secondary trigger hash: udptx.c sha256 7a0b8577212a0aa849cb09e9d878d3e36911b748d0ef457bf3806971437f0245. Unprivileged tx survived because the freed chunks stale if_flags/if_link_state read inactive and failover fell through to the live port - the freed-memory reads did occur; crash on that path is content-dependent. Guest reset to clean with-src snapshot after validation.",
 "recommended_fix": "Register/Deregister lagg_port_ifdetach on ifnet_detach_event (the event if_detach actually fires) - see fix.diff; optionally assert at registration that the event list is the live one.",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Fri Sep 11 04:59:12 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (make nativekernel KERNCONF=X86_64_GENERIC with fix.diff applied to sys/net/lagg/if_lagg.c)",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "Baseline stock kernel #0: stale member after destroy + Fatal trap 12 in tapifioctl via laggproto lacp. Patched kernel #1 (fix.diff applied in-guest, nativekernel rebuild, reboot): ifconfig tap0 destroy cleanly removes the port (unpriv SIOCGLAGG: ports=1, no stale entry, tap1 becomes MASTER); laggproto lacp and roundrobin switches succeed; guest up. Bad behavior gone.",
 "fix_evidence": "findings/poc/DF-3344/fix_validation.log (full sequence on kernel #1) vs run.log + panic.txt (kernel #0)"
}