⬢ DragonFlyBSD Kernel Audit
DF-4310 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/netif/ste/if_ste.c
+++ b/sys/dev/netif/ste/if_ste.c
@@ -787,29 +787,46 @@
 	ifp = &sc->arpcom.ac_if;
 	mii = device_get_softc(sc->ste_miibus);
 
-	lwkt_serialize_enter(ifp->if_serializer);
-
-        IFNET_STAT_INC(ifp, collisions, CSR_READ_1(sc, STE_LATE_COLLS)
-            + CSR_READ_1(sc, STE_MULTI_COLLS)
-            + CSR_READ_1(sc, STE_SINGLE_COLLS));
-
-	if (!sc->ste_link) {
-		mii_pollstat(mii);
-		if (mii->mii_media_status & IFM_ACTIVE &&
-		    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
-			sc->ste_link++;
-			/* 
-			 * we don't get a call-back on re-init so do it 
-			 * otherwise we get stuck in the wrong link state
-			 */
-			ste_miibus_statchg(sc->ste_dev);
-			if (!ifq_is_empty(&ifp->if_snd))
-				if_devstart(ifp);
+	/*
+	 * DF-4310 (DF-3715 family): must NOT block on if_serializer from
+	 * this callout callback.  ste_stop() and ste_intr()'s STATS_OFLOW
+	 * path issue the *synchronous* callout_stop(&sc->ste_stat_timer)
+	 * while holding if_serializer; a dequeued callback parked in
+	 * lwkt_serialize_enter() deadlocks against them (AB-BA, both
+	 * threads sleep forever) and, being a non-MP callout, it parks
+	 * while holding the MP lock.  Skip this tick's work when the
+	 * serializer is contended.
+	 */
+	if (lwkt_serialize_try(ifp->if_serializer)) {
+		IFNET_STAT_INC(ifp, collisions, CSR_READ_1(sc, STE_LATE_COLLS)
+		    + CSR_READ_1(sc, STE_MULTI_COLLS)
+		    + CSR_READ_1(sc, STE_SINGLE_COLLS));
+
+		if (!sc->ste_link) {
+			mii_pollstat(mii);
+			if (mii->mii_media_status & IFM_ACTIVE &&
+			    IFM_SUBTYPE(mii->mii_media_active) != IFM_NONE) {
+				sc->ste_link++;
+				/*
+				 * we don't get a call-back on re-init so do
+				 * it otherwise we get stuck in the wrong
+				 * link state
+				 */
+				ste_miibus_statchg(sc->ste_dev);
+				if (!ifq_is_empty(&ifp->if_snd))
+					if_devstart(ifp);
+			}
 		}
+		lwkt_serialize_exit(ifp->if_serializer);
 	}
 
+	/*
+	 * Re-arm OUTSIDE the serializer: a recursive callout_reset() from
+	 * inside the callback overrides a racing callout_stop() (RESET
+	 * beats STOP in kern_timeout.c) and would resurrect the timer
+	 * after ste_stop()/ste_detach() (post-detach use-after-free).
+	 */
 	callout_reset(&sc->ste_stat_timer, hz, ste_stats_update, sc);
-	lwkt_serialize_exit(ifp->if_serializer);
 }