DragonFlyBSD Kernel Audit
DF-2000 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/netif/ic/if_ic.c b/sys/dev/netif/ic/if_ic.c
--- a/sys/dev/netif/ic/if_ic.c
+++ b/sys/dev/netif/ic/if_ic.c
@@ -82,6 +82,8 @@
 	int ic_xfercnt;
 
 	int ic_iferrs;
+
+	struct lwkt_token ic_tok;	/* serializes obuf/ifbuf/cp vs SIOCSIFMTU */
 };
 
 static devclass_t ic_devclass;
@@ -130,6 +132,8 @@
 	struct ic_softc *sc = (struct ic_softc *)device_get_softc(dev);
 	struct ifnet *ifp = &sc->ic_if;
 
+	lwkt_token_init(&sc->ic_tok, "ic_tok");
+
 	sc->ic_addr = PCF_MASTER_ADDRESS;	/* XXX only PCF masters */
 
 	ifp->if_softc = sc;
@@ -203,6 +207,14 @@
 	break;
 
     case SIOCSIFMTU:
+	/*
+	 * icoutput() and icintr() dereference sc->ic_obuf / sc->ic_ifbuf
+	 * (and capture sc->ic_cp from ic_ifbuf).  Serialize the buffer
+	 * swap-and-free against both paths so a concurrent TX/RX on another
+	 * CPU never holds a stale pointer into the freed M_DEVBUF chunk.
+	 */
+	lwkt_gettoken(&sc->ic_tok);
+
 	/* save previous buffers */
 	iptr = sc->ic_ifbuf;
 	optr = sc->ic_obuf;
@@ -220,6 +232,8 @@
 	    kfree(optr,M_DEVBUF);
 
 	sc->ic_if.if_mtu = ifr->ifr_mtu;
+
+	lwkt_reltoken(&sc->ic_tok);
 	break;
 
     case SIOCGIFMTU:
@@ -260,6 +274,8 @@
 	
 	crit_enter();
 
+	lwkt_gettoken(&sc->ic_tok);
+
 	switch (event) {
 
 	case INTR_GENERAL:
@@ -326,6 +342,7 @@
 		panic("%s: unknown event (%d)!", __func__, event);
 	}
 
+	lwkt_reltoken(&sc->ic_tok);
 	crit_exit();
 }
 
@@ -348,6 +365,8 @@
 
 	crit_enter();
 
+	lwkt_gettoken(&sc->ic_tok);
+
 	/* already sending? */
 	if (sc->ic_sending) {
 		IFNET_STAT_INC(ifp, oerrors, 1);
@@ -386,6 +405,12 @@
 
 	crit_exit();
 
+	/*
+	 * sc->ic_obuf is read by iicbus_block_write below; keep the token
+	 * across it so a concurrent SIOCSIFMTU cannot kfree the buffer
+	 * out from under the in-flight bus write.
+	 */
+
 	/* send the packet */
 	if (iicbus_block_write(parent, sc->ic_addr, sc->ic_obuf,
 				len + ICHDRLEN, &sent))
@@ -398,10 +423,13 @@
 
 	sc->ic_sending = 0;
 
+	lwkt_reltoken(&sc->ic_tok);
+
 	return (0);
 
 error:
 	m_freem(m);
+	lwkt_reltoken(&sc->ic_tok);
 	crit_exit();
 
 	return(0);