--- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -112,6 +112,53 @@ #include +/* ------------------------------------------------------------------ */ +/* DF-3389 verification instrumentation (audit-only, NOT for upstream) */ +#include + +#define DF3389_POISON 0xDF3389A6u +#define DF3389_RINGSZ 2048 /* power of two */ + +struct df3389_ent { + void *ptr; +}; + +static struct df3389_ent df3389_ring[DF3389_RINGSZ]; +static volatile u_int df3389_idx; +static u_long df3389_hits; +static u_long df3389_defers; +static u_long df3389_captures; + +void (*df3389_freehook)(void *, int); + +SYSCTL_DECL(_net_inet6_ip6); +SYSCTL_QUAD(_net_inet6_ip6, OID_AUTO, df3389_hits, CTLFLAG_RD, + &df3389_hits, 0, "DF3389 verified UAF dereferences of freed in6_ifaddr"); +SYSCTL_QUAD(_net_inet6_ip6, OID_AUTO, df3389_defers, CTLFLAG_RD, + &df3389_defers, 0, "DF3389 transport6 defers observed"); +SYSCTL_QUAD(_net_inet6_ip6, OID_AUTO, df3389_captures, CTLFLAG_RD, + &df3389_captures, 0, "DF3389 ip6_setdstifaddr stores observed"); + +/* + * Called from ifac_free() immediately before the final kfree() of an + * ifaddr: poison in6_ifaddr flags and record the pointer so that any + * later dereference through the m_tag borrow is detectable. + */ +void df3389_free_noter(void *, int); + +void +df3389_free_noter(void *p, int fam) +{ + u_int idx; + + if (fam != AF_INET6) + return; + idx = atomic_fetchadd_int(&df3389_idx, 1); + df3389_ring[idx & (DF3389_RINGSZ - 1)].ptr = p; + ((struct in6_ifaddr *)p)->ia6_flags = DF3389_POISON; +} +/* ---------------- end DF-3389 instrumentation --------------------- */ + extern struct domain inet6domain; extern struct protosw inet6sw[]; @@ -172,6 +219,7 @@ } netisr_register(NETISR_IPV6, ip6_input, NULL); /* XXX hashfn */ + df3389_freehook = df3389_free_noter; scope6_init(); addrsel_policy_init(); nd6_init(); @@ -777,10 +825,38 @@ transport6_processing_handler(netmsg_t netmsg) { struct netmsg_packet *pmsg = (struct netmsg_packet *)netmsg; + struct ip6aux *dfn; struct protosw *sw6; int hlen; int nxt; + /* DF-3389: deferred consumer side. If the m_tag still holds an + * ia6 pointer that has since been freed (poisoned), the claimed + * UAF read happens right here — record it. */ + dfn = ip6_findaux(pmsg->nm_packet); + if (dfn && dfn->ip6a_dstia6 != NULL) { + struct in6_ifaddr *ia6 = dfn->ip6a_dstia6; + u_int fl = ia6->ia6_flags; /* the claimed UAF read */ + + if (fl == DF3389_POISON) { + int k, confirmed = 0; + + for (k = 0; k < DF3389_RINGSZ; ++k) { + if (df3389_ring[k].ptr == (void *)ia6) { + confirmed = 1; + break; + } + } + if (confirmed) { + df3389_hits++; + kprintf("DF3389: UAF-HIT ia6=%p ia6_flags=%#x " + "(freed in6_ifaddr dereferenced by " + "deferred transport6)\n", ia6, fl); + } + } + } + df3389_defers++; + sw6 = &inet6sw[ip6_protox[pmsg->nm_nxt]]; hlen = pmsg->base.lmsg.u.ms_result; @@ -801,6 +877,7 @@ n = ip6_addaux(m); if (n) n->ip6a_dstia6 = ia6; + df3389_captures++; return n; /* NULL if failed to set */ } --- a/sys/net/if.c +++ b/sys/net/if.c @@ -3482,9 +3482,15 @@ KASSERT(ifa->ifa_ncnt > 0 && ifa->ifa_ncnt <= ncpus, ("invalid # of ifac, %d", ifa->ifa_ncnt)); if (atomic_fetchadd_int(&ifa->ifa_ncnt, -1) == 1) { + /* DF-3389 audit instrumentation */ + extern void (*df3389_freehook)(void *, int); + #ifdef IFADDR_DEBUG kprintf("free ifa %p\n", ifa); #endif + if (df3389_freehook) + df3389_freehook(ifa, + ifa->ifa_addr ? ifa->ifa_addr->sa_family : 0); kfree(ifa->ifa_containers, M_IFADDR); kfree(ifa, M_IFADDR); }