DF-3387 / verdict.json
{ "finding_id": "DF-3387", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "Reproduced 2/2 on the stock INVARIANTS guest: lagg_input() (sys/net/lagg/if_lagg.c:1466-1470) frees the mbuf and NULLs it under IFF_MONITOR, then falls through to ether_reinput_oncpu(scifp, NULL, REINPUT_RUNBPF); with the lagg master IFF_UP, ether_reinput_oncpu (sys/net/if_ethersubr.c:1317-1320) writes ifp to m->m_pkthdr.rcvif of a NULL mbuf -> Fatal trap 12 at vaddr 0xa0, Stopped at ether_reinput_oncpu+0x1a: movq %rdi,0xa0(%rsi). Trigger is a single frame received on any slave (write(2) to /dev/tapN = RX injection via tapwrite -> if_input); setup is privileged ifconfig. Write-only fault at fixed near-NULL address: clean DoS panic, no controllable primitive, no escalation path.", "exploit_chain": "ifconfig tap0 create/up; ifconfig lagg0 create; ifconfig lagg0 laggproto failover; ifconfig lagg0 laggport tap0 (slave if_type becomes IFT_IEEE8023ADLAG -> RX diverted to lagg_input at if_ethersubr.c:1076 before L3); ifconfig lagg0 up monitor; write 34 bytes (bcast ether + minimal IPv4) to /dev/tap0 -> deterministic panic on the first received packet. Remote variant: once configured, any frame arriving on any slave (e.g. ambient broadcast) panics the kernel.", "evidence": [ "panic.txt / panic.2.txt: Fatal trap 12 ... fault virtual address = 0xa0 ... Stopped at ether_reinput_oncpu+0x1a: movq %rdi,0xa0(%rsi)", "run.2.log: all 7 ifconfig steps rc=0, connection dropped immediately after (write() executed, output lost to the panic)", "panic.2.txt serial mirror: [df3387] + ifconfig lagg0 up monitor -> rc=0 directly followed by the panic", "fix.diff: two hunks (if_lagg.c return-after-monitor-discard; if_ethersubr.c defensive NULL guard)", "run.patched.log + build.log: patched kernel #1, same PoC -> NO PANIC - bug did not reproduce, guest stays up" ], "kernel_refs": [ "sys/net/lagg/if_lagg.c:1462", "sys/net/lagg/if_lagg.c:1466", "sys/net/lagg/if_lagg.c:1470", "sys/net/lagg/if_lagg.c:674", "sys/net/if_ethersubr.c:1303", "sys/net/if_ethersubr.c:1317", "sys/net/if_ethersubr.c:1320", "sys/net/if_ethersubr.c:1076" ], "poc_changes": "Seed (none existed for this bug; written fresh). Frame content beyond the 14-byte header is irrelevant since the IFT_IEEE8023ADLAG branch in ether_demux_oncpu runs before any L3 processing; a 34-byte bcast/ETHERTYPE_IP frame is used for realism. Progress lines mirrored to /dev/console because the panic eats unflushed ssh output.", "attempts": 2, "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": 138, "guest_dirty": 0, "build_cmd": "cc -O -g -Wall -o /root/df3387_trigger /root/df3387/trigger.c", "run_cmd": "/root/df3387_trigger", "code_hash": "a63e094858d0c393561618c44320dc57c06a76931718573bd0e3679505d8a2e9", "notes": "Same profile as DF-0445 (privileged config + traffic-triggered panic, Medium). Distinct from DF-0367 (unlocked ifp->if_lagg deref, lines 1442-1456). Other ether_reinput_oncpu callers (vlan/carp/bridge) never pass NULL; lagg is the outlier violating the if_ethersubr.c contract. Guest reset to clean-source snapshot after validation.", "recommended_fix": "if_lagg.c: return immediately after the IFF_MONITOR discard (do not reinput a consumed mbuf); if_ethersubr.c: defensive NULL check at top of ether_reinput_oncpu.", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Fri Sep 11 23:54:18 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied in-guest to /usr/src, make -j8 nativekernel KERNCONF=X86_64_GENERIC + make installkernel, rebooted into #1. Exact PoC re-run: frame harmlessly discarded by the monitor path, NO PANIC - bug did not reproduce printed, lagg0/tap0 destroyed cleanly, guest remained up and ssh-able. Baseline panicked 2/2 on the same input.", "fix_evidence": "run.patched.log (full PoC output on patched kernel), build.log (kernel build tail + uname)" } |