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)
PoC verification
Evidence pack
findings/poc/DF-3463 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
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_testableFix: clear/purge reass queues on if_detach or take an ifnet reference for queued fragments; not A/B-able without an observable recycle.
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.
No comments yet.