DragonFlyBSD Kernel Audit
DF-0503 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/netgraph7/ng_fec.c b/sys/netgraph7/ng_fec.c
--- a/sys/netgraph7/ng_fec.c
+++ b/sys/netgraph7/ng_fec.c
@@ -101,6 +101,8 @@
 #include <sys/queue.h>
 
 #include <net/if.h>
+#include <net/if_var.h>
+#include <sys/eventhandler.h>
 #include <net/if_dl.h>
 #include <net/if_types.h>
 #include <net/if_media.h>
@@ -346,6 +348,38 @@
 			INTERFACE STUFF
  ************************************************************************/
 
+static void	ng_fec_ifnet_detach_event(void *, struct ifnet *);
+
+/*
+ * ifnet_detach_event handler: walks every fec bundle and NULLs any
+ * stored member-ifp that matches the detaching interface.  Without
+ * this, the stored p->fec_if becomes a dangling pointer that
+ * ng_fec_tick / ng_fec_input / ng_fec_start dereference via UAF.
+ * See DF-0503.
+ */
+static void
+ng_fec_ifnet_detach_event(void *arg, struct ifnet *detaching_ifp)
+{
+	struct ng_fec_private *priv;
+
+	mtx_lock(&ng_fec_mtx);
+	LIST_FOREACH(priv, &ng_fec_units, fec_next) {
+		struct ng_fec_bundle *b = &priv->fec_bundle;
+		struct ng_fec_portlist *p;
+
+		TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) {
+			if (p->fec_if == detaching_ifp) {
+				kprintf("fec%d: member iface %s detached; "
+				    "clearing stored pointer\n",
+				    priv->unit, detaching_ifp->if_xname);
+				p->fec_if = NULL;
+				/* keep fec_if_input for restore-on-re-add */
+			}
+		}
+	}
+	mtx_unlock(&ng_fec_mtx);
+}
+
 static int
 ng_fec_addport(struct ng_fec_private *priv, char *iface)
 {
@@ -366,13 +400,17 @@
 		return (EINVAL);
 	}
 
-	/* Find the interface */
+	/* Find the interface.  Per if_var.h:894 ifunit() requires
+	 * ifnet_lock held before AND while accessing the returned ifp. */
+	ifnet_lock();
 	bifp = ifunit(iface);
 	if (bifp == NULL) {
+		ifnet_unlock();
 		kprintf("fec%d: tried to add iface %s, which "
 		    "doesn't seem to exist\n", priv->unit, iface);
 		return(ENOENT);
 	}
+	ifnet_unlock();
 
 	/* See if we have room in the bundle */
 	if (b->fec_ifcnt == FEC_BUNDLESIZ) {
@@ -477,13 +515,16 @@
 		return (EINVAL);
 	}
 
-	/* Find the interface */
+	/* Find the interface (ifnet_lock required per if_var.h:894). */
+	ifnet_lock();
 	bifp = ifunit(iface);
 	if (bifp == NULL) {
+		ifnet_unlock();
 		kprintf("fec%d: tried to remove iface %s, which "
 		    "doesn't seem to exist\n", priv->unit, iface);
 		return(ENOENT);
 	}
+	ifnet_unlock();
 
 	TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) {
 		if (p->fec_if == bifp)
@@ -685,6 +726,9 @@
 	TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) {
 		bzero((char *)&ifmr, sizeof(ifmr));
 		ifp = p->fec_if;
+		/* DF-0503: member iface may have been detached. */
+		if (ifp == NULL)
+			continue;
 		error = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t)&ifmr);
 		if (error) {
 			kprintf("fec%d: failed to check status "
@@ -1064,6 +1108,8 @@
 	}
 
 	TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) {
+		if (p->fec_if == NULL)
+			continue;
 		if (port == p->fec_idx)
 			break;
 	}
@@ -1356,11 +1402,21 @@
 {
 	int error = 0;
 
+	static eventhandler_tag ifnet_detach_tag = NULL;
+
 	switch (event) {
 	case MOD_LOAD:
 		mtx_init(&ng_fec_mtx, "ng_fec", NULL, MTX_DEF);
+		/* DF-0503: register for interface departure events so we can
+		 * NULL stored member-ifp pointers before the ifnet is freed. */
+		ifnet_detach_tag = EVENTHANDLER_REGISTER(ifnet_detach_event,
+		    ng_fec_ifnet_detach_event, NULL, EVENTHANDLER_PRI_ANY);
 		break;
 	case MOD_UNLOAD:
+		if (ifnet_detach_tag != NULL) {
+			EVENTHANDLER_DEREGISTER(ifnet_detach_event, ifnet_detach_tag);
+			ifnet_detach_tag = NULL;
+		}
 		mtx_destroy(&ng_fec_mtx);
 		break;
 	default: