DF-5795 / verdict.json
{ "finding_id": "DF-5795", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "likely", "verdict": "Deterministically reproduced on the production (non-INVARIANTS) guest: m_move_pkthdr() (sys/kern/uipc_mbuf.c:2551) never clears M_PKTHDR on the source mbuf, violating its documented contract (sys/sys/mbuf.h:406-410, \"from no longer has a pkthdr after this operation\") and FreeBSD macro semantics. The source keeps a live stale pkthdr (demonstrated: pkthdr.len 30 mutated to 22 by m_adj() after the move, the exact icmp6.c:559/563 sequence reachable by any ICMPv6 echo request); every M_MOVE_PKTHDR call site in the tree glues this duplicate header mid-chain. No in-tree memory-safety sink was found after auditing all pkthdr readers (heads only), so impact is kernel-state/contract corruption with stale metadata on remote-reachable paths, not memory corruption.", "exploit_chain": "", "evidence": "run.log decisive lines: DF5795: after move : from.flags=00008002 ... M_PKTHDR-on-from=1, DF5795: REPRODUCED: source mbuf still carries M_PKTHDR after M_MOVE_PKTHDR, DF5795: m_adj(from, 8) mutated the moved pkthdr.len: 30 -> 22 (MUTATED). Source: mbuf_probe.c probe_5795(); root-cause at sys/kern/uipc_mbuf.c:2551-2558.", "kernel_refs": [ "sys/kern/uipc_mbuf.c:2551", "sys/kern/uipc_mbuf.c:1882", "sys/sys/mbuf.h:406", "sys/netinet6/icmp6.c:559", "sys/netinet6/ip6_input.c:255", "sys/netinet6/ip6_output.c:3072" ], "poc_changes": "Seed concept converted to a KLD invariant probe; added missing #include <sys/malloc.h> for M_WAITOK (first build failed without it); used m_gethdr/m_get chain with pkthdr.len=30 to show the stale header being mutated by m_adj() post-move.", "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": 95, "guest_dirty": 0, "build_cmd": "cd /tmp/df5795 && make -m /usr/src/share/mk SYSDIR=/usr/src/sys", "run_cmd": "kldload ./mbuf_probe.ko; sleep 1; kldunload mbuf_probe; dmesg | tail -30", "code_hash": "4d8206b6042649863367cdcdc683d06582fbd5d644d9f206a89cdf642a19e499", "notes": "KKASSERT/KASSERT are INVARIANTS-only on this kernel, so nothing masks the divergence in production. Guest left clean (module unloaded, no kernel changes). Impact field none reflects no demonstrated memory unsafety; the demonstrated fact is a live stale packet-header mid-chain on remote-reachable paths.", "recommended_fix": "Clear M_PKTHDR on the source in m_move_pkthdr(): from->m_flags &= ~M_PKTHDR; (do NOT touch M_EXT/m_data - DF callers rely on the cluster staying attached; see fix.diff which also corrects the stale FreeBSD-macro comment in m_unshare).", "fix_status": "not_testable", "fix_baseline_reproduced": 1, "fix_patched_reproduced": null, "fix_verdict": "Fix-validation kernel rebuild not performed: finding is not a reproduced memory-corruption (severity Low), so the mandatory rebuild cycle does not apply. fix.diff is a one-line flag clear audited against all seven in-tree M_MOVE_PKTHDR call sites; expected patched behavior: M_PKTHDR-on-from=0, m_adj leaves the source untouched.", "fix_evidence": "fix.diff in this directory; call-site audit in VERDICT.md." } |