DragonFlyBSD Kernel Audit
DF-0366 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/net/lagg/if_lagg.c b/sys/net/lagg/if_lagg.c
--- a/sys/net/lagg/if_lagg.c
+++ b/sys/net/lagg/if_lagg.c
@@ -1755,15 +1755,25 @@
 	ASSERT_ALTQ_SQ_DEFAULT(ifp, ifsq);
 	ASSERT_ALTQ_SQ_SERIALIZED_HW(ifsq);
 
-	if (((ifp->if_flags & IFF_RUNNING) == 0)
-	    || (sc->sc_proto == LAGG_PROTO_NONE)
-            || (sc->sc_count == 0)) {
+	/*
+	 * Re-validate sc_count INSIDE LAGG_RLOCK.  The old lockless
+	 * early-exit check at this point raced lagg_port_destroy's
+	 * sc_count-- (under WLOCK) - if count went 1->0 between the
+	 * lockless check and the RLOCK acquire, the round-robin (and
+	 * loadbalance) select_tx_port routines divide by zero.
+	 */
+	if ((ifp->if_flags & IFF_RUNNING) == 0
+	    || sc->sc_proto == LAGG_PROTO_NONE) {
 		ifsq_purge(ifsq);
 		return;
 	}
 
-	
         LAGG_RLOCK(sc);
+	if (sc->sc_count == 0) {
+		LAGG_RUNLOCK(sc);
+		ifsq_purge(ifsq);
+		return;
+	}
 	for (;;) {
 		m = ifsq_dequeue(ifsq);
 		if (m == NULL){