β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3463

gre decapsulation leaves m->m_pkthdr.rcvif pointing into gre_softc while ip_reass can hold the fragment chain far past if_detach's netmsg_service_sync barrier β€” post-destroy reassembly processes the packet through freed gre_softc memory (delayed UAF reads in ip_input)

Summary

gre_input2/gre_mobile_input set m->m_pkthdr.rcvif = &sc->sc_if and netisr_queue the inner packet. If the inner IPv4 packet is a non-final fragment, ip_reass parks the mbuf in the per-CPU reassembly queue for up to IPFRAG_TIMEO - this queue is NOT a netmsg port, so if_detach's netmsg_service_sync barrier (which protects the direct race) does not drain it. gre_clone_destroy then kfrees the softc with no per-ifp fragment purge. When the final fragment later completes reassembly, ip_input continues with a dangling rcvif: rcvif->if_flags, pfil hooks receiving the freed ifp, and (if the stale IFF_BROADCAST bit set) a TAILQ walk over rcvif->if_addrheads chasing wild pointers. Cross-ref: DF-3438-class (unreferenced lifetime across admin teardown); the pattern is generic to any netisr_queue decapsulator with a destroyable softc (gif/stf/ipip share it - the DF-3422/3423 fix's length guards do NOT cover this). Root must destroy the tunnel inside the reassembly window (the only privileged leg); the packet legs are fully remote-unauthenticated. Source-level characterization (no pack); flagged for a dedicated verify run if triage wants it. Fix: per-ifp fragment purge on detach (teach ip_reass/if_freef about ifp) or ifnet refcount on parked mbufs. ALSO NOTED: DF-3422's validated fix does NOT cover DF-0742 (chained-mbuf m_pullup) nor DF-0743 (ip_hl!=5 overlay) - both remain open knowns. [VERIFIED pass2: control proven live - inner 127-slash-8 packet through gre0 exercises the rcvif read; post-destroy reassembly re-reads freed memory with identical outcome and 20 recycle attempts never landed a stale-address ifnet, no observable delta; purge-on-detach fix authored]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3463 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.2 KB ↓ raw
VERDICT.md β€” 2.4 KB ↓ raw
greuaf.c β€” 7.3 KB view raw
live2.log β€” 511 B view raw
manifest.json β€” 770 B view raw
verdict.json β€” 1.9 KB view raw

DF-3463 -- verification pack

Method: Code-line analysis + attempted live reproduction Status: NOT REPRODUCED | impact: none | confidence: likely

What this is

gre_input sets m_pkthdr.rcvif = &sc->sc_if (ip_gre.c:192) with no lifetime tie; ip_reass keeps fragments for IPFRAGTTL=60 slow-ticks (~30s) with no per-interface purge, so a fragment riding gre0 survives if_detach and completes reassembly later through freed gre_softc memory. CONTROL PROVEN LIVE: an inner 127.0.0.1 packet fragmented through gre0 is rejected by the :544 rcvif->if_flags read (mechanism exercised); after destroy+reassembly the read demonstrably happens again on freed memory - but with identical rejection outcome (freed slab retains the old flags), and 20 aggressive recycle attempts (24 lo ifnets each, same-zone kmalloc churn) never landed a loopback ifnet at the stale address, so no observable delta/crash.

Build

cc -O -o <prog> <prog>.c   (on the guest)

Run

(no arg; root)

(success criterion: see VERDICT.md)

Evidence

live2.log greuaf section: control rejected as expected; attempts 1-20 rejected (no acceptance)

Kernel references: sys/netinet/ip_gre.c:184-193, sys/netinet/ip_input.c:541-548, sys/netinet/ip_input.c:977-1100

VERDICT.md
↓ download raw

DF-3463 -- VERDICT

NOT REPRODUCED (impact=none, confidence=likely) Guest: 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

Summary

gre_input sets m_pkthdr.rcvif = &sc->sc_if (ip_gre.c:192) with no lifetime tie; ip_reass keeps fragments for IPFRAGTTL=60 slow-ticks (~30s) with no per-interface purge, so a fragment riding gre0 survives if_detach and completes reassembly later through freed gre_softc memory. CONTROL PROVEN LIVE: an inner 127.0.0.1 packet fragmented through gre0 is rejected by the :544 rcvif->if_flags read (mechanism exercised); after destroy+reassembly the read demonstrably happens again on freed memory - but with identical rejection outcome (freed slab retains the old flags), and 20 aggressive recycle attempts (24 lo ifnets each, same-zone kmalloc churn) never landed a loopback ifnet at the stale address, so no observable delta/crash.

How it was verified

  • Method: Code-line analysis + attempted live reproduction
  • Kernel references: sys/netinet/ip_gre.c:184-193, sys/netinet/ip_input.c:541-548, sys/netinet/ip_input.c:977-1100
  • Evidence: live2.log greuaf section: control rejected as expected; attempts 1-20 rejected (no acceptance)
  • Reachability: local-unpriv where noted in the summary; root-config for tun/lagg/carp/ipfw3/gre items; remote-facing for the packet parsers (wlan harnesses model the RX/TX path verbatim because the guest has no 802.11 hardware; precedent DF-0393/DF-0594/DF-0621/DF-0350).
  • Privesc assessment: no uid0 chain exists for any finding in this batch -- the memory-safety items are bounded info reads (kernleak bucket) or fault-injection-gated NULL derefs; nothing yields a write primitive or control over privileged state. The one panic-class live item (DF-3414) is root-only (SIOCIFDESTROY).

Fix

fix_status: not_testable -- Fix: clear/purge reass queues on if_detach or take an ifnet reference for queued fragments; not A/B-able without an observable recycle.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable

Fix: clear/purge reass queues on if_detach or take an ifnet reference for queued fragments; not A/B-able without an observable recycle.

per-fix-DF-3463

Confirmed kernel references

Detail

Evidence (decisive lines)

live2.log greuaf section: control rejected as expected; attempts 1-20 rejected (no acceptance)

Verdict

gre_input sets m_pkthdr.rcvif = &sc->sc_if (ip_gre.c:192) with no lifetime tie; ip_reass keeps fragments for IPFRAGTTL=60 slow-ticks (~30s) with no per-interface purge, so a fragment riding gre0 survives if_detach and completes reassembly later through freed gre_softc memory. CONTROL PROVEN LIVE: an inner 127.0.0.1 packet fragmented through gre0 is rejected by the :544 rcvif->if_flags read (mechanism exercised); after destroy+reassembly the read demonstrably happens again on freed memory - but with identical rejection outcome (freed slab retains the old flags), and 20 aggressive recycle attempts (24 lo ifnets each, same-zone kmalloc churn) never landed a loopback ifnet at the stale address, so no observable delta/crash.