⬢ DragonFlyBSD Kernel Audit
DF-3389 / df3389_canary.diff
← back to finding ↓ download raw
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -112,6 +112,53 @@
 
 #include <net/net_osdep.h>
 
+/* ------------------------------------------------------------------ */
+/* DF-3389 verification instrumentation (audit-only, NOT for upstream) */
+#include <sys/sysctl.h>
+
+#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);
 	}