⬢ DragonFlyBSD Kernel Audit
DF-5847 / diag5847.diff
← back to finding ↓ download raw
--- nd6.c.orig	2026-10-26 00:00:00.000000000 +0000
+++ nd6.c	2026-10-26 00:00:00.000000000 +0000
@@ -141,6 +141,44 @@
 struct nd_prhead nd_prefix = { 0 };
 struct mtx nd6_mtx = MTX_INITIALIZER("nd6");
 
+/* DF-5847 DIAGNOSTIC ONLY -- never for upstream */
+struct nd6dbg {
+	int		op;		/* 1=add 2=del 3=resolve 4=nd6free */
+	int		cpu;
+	void		*ln;
+	void		*rt;
+	void		*rt_llinfo;
+	long		t;		/* ticks */
+	void		*ln_prev;
+	void		*ln_next;
+};
+static struct nd6dbg nd6dbg_ring[64];
+static volatile int nd6dbg_idx;
+
+static void
+nd6dbg_log(int op, void *lnp, void *rtp)
+{
+	struct nd6dbg *d;
+	struct llinfo_nd6 *ln = lnp;
+	int i = atomic_fetchadd_int(&nd6dbg_idx, 1);
+
+	d = &nd6dbg_ring[i & 63];
+	d->op = op;
+	d->cpu = mycpuid;
+	d->ln = lnp;
+	d->rt = rtp;
+	d->rt_llinfo = rtp ? ((struct rtentry *)rtp)->rt_llinfo : NULL;
+	d->t = ticks;
+	d->ln_prev = ln ? ln->ln_prev : NULL;
+	d->ln_next = ln ? ln->ln_next : NULL;
+}
+
+static void
+nd6dbg_dump(void)
+{
+	int i, n;
+
+	kprintf("DF5847: ring dump (idx=%d):\n", nd6dbg_idx);
+	n = nd6dbg_idx;
+	for (i = n - 16; i < n; i++) {
+		struct nd6dbg *d = &nd6dbg_ring[i & 63];
+		kprintf("DF5847: [%d] op=%d cpu=%d ln=%p rt=%p rt_llinfo=%p "
+		    "ln_prev=%p ln_next=%p t=%ld\n",
+		    i, d->op, d->cpu, d->ln, d->rt, d->rt_llinfo,
+		    d->ln_prev, d->ln_next, d->t);
+	}
+}
+/* END DF-5847 DIAGNOSTIC */
+
 int nd6_recalc_reachtm_interval = ND6_RECALC_REACHTM_INTERVAL;
 
 #define SIN6(s) ((struct sockaddr_in6 *)s)
@@ -486,6 +524,22 @@
 		/* sanity check */
 		if (!rt)
 			panic("rt=0 in nd6_timer(ln=%p)", ln);
+		/* DF-5847 DIAGNOSTIC: catch walk-over-unlinked (ln_prev==NULL
+		 * is the RTM_DELETE tombstone) before the llinfo compare. */
+		if (ln->ln_prev == NULL) {
+			kprintf("DF5847: timer walked unlinked ln=%p rt=%p "
+			    "cpu=%d\n", ln, rt, mycpuid);
+			nd6dbg_dump();
+			panic("DF5847 walk-over-unlinked ln=%p", ln);
+		}
+		if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln) {
+			kprintf("DF5847: invariant trip ln=%p rt_llinfo=%p "
+			    "cpu=%d\n", ln, rt->rt_llinfo, mycpuid);
+			nd6dbg_dump();
+			panic("rt_llinfo(%p) is not equal to ln(%p)",
+			      rt->rt_llinfo, ln);
+		}
 		if (rt->rt_llinfo && (struct llinfo_nd6 *)rt->rt_llinfo != ln)
 			panic("rt_llinfo(%p) is not equal to ln(%p)",
 			      rt->rt_llinfo, ln);
@@ -1028,6 +1082,9 @@
 	struct llinfo_nd6 *ln = (struct llinfo_nd6 *)rt->rt_llinfo, *next;
 	struct nd_defrouter *dr;
 	struct in6_addr in6 = ((struct sockaddr_in6 *)rt_key(rt))->sin6_addr;
 
+	nd6dbg_log(4, (void *)ln, (void *)rt);
+
 	/*
 	 * we used to have kpfctlinput(PRC_HOSTDEAD) here.
 	 * even though it is not harmful, it was not really necessary.
@@ -1300,6 +1357,8 @@
 		nd6_inuse++;
 		nd6_allocated++;
 		bzero(ln, sizeof(*ln));
+		nd6dbg_log(req == RTM_ADD ? 1 : 3, (void *)ln, (void *)rt);
+
 		ln->ln_rt = rt;
 		/* this is required for "ndp" command. - shin */
 		if (req == RTM_ADD) {
@@ -1410,6 +1469,8 @@
 			in6m = IN6_LOOKUP_MULTI(&llsol, ifp);
 			if (in6m)
 				in6_delmulti(in6m);
 	}
+	nd6dbg_log(2, (void *)ln, (void *)rt);
 	nd6_inuse--;
 	ln->ln_next->ln_prev = ln->ln_prev;
 	ln->ln_prev->ln_next = ln->ln_next;