⬢ DragonFlyBSD Kernel Audit
DF-4055 / fix.diff
← back to finding ↓ download raw
diff -ruN a/sys/dev/netif/et/if_et.c b/sys/dev/netif/et/if_et.c
--- a/sys/dev/netif/et/if_et.c	2026-10-14 21:09:30.985581623 +0000
+++ b/sys/dev/netif/et/if_et.c	2026-10-14 21:09:30.989581572 +0000
@@ -1848,6 +1848,19 @@
 	rxst_wrap = (rxs_stat_ring & ET_RXS_STATRING_WRAP) ? 1 : 0;
 	rxst_index = __SHIFTOUT(rxs_stat_ring, ET_RXS_STATRING_INDEX);
 
+	/*
+	 * The DMA'd stat-ring index field (ET_RXS_STATRING_INDEX) is 12
+	 * bits wide while the software stat ring has ET_RX_NSTAT (1024)
+	 * entries.  An out-of-range value can never equal rsr_index and
+	 * would spin the completion walk forever under the serializer
+	 * (DF-4056).
+	 */
+	if (rxst_index >= ET_RX_NSTAT) {
+		IFNET_STAT_INC(ifp, ierrors, 1);
+		if_printf(ifp, "invalid RX stat ring index %d\n", rxst_index);
+		return;
+	}
+
 	while (rxst_index != rxst_ring->rsr_index ||
 	       rxst_wrap != rxst_ring->rsr_wrap) {
 		struct et_rxbuf_data *rbd;
@@ -1893,13 +1906,30 @@
 				m_freem(m);
 				IFNET_STAT_INC(ifp, ierrors, 1);
 			} else {
-				m->m_pkthdr.len = m->m_len = buflen;
-				m->m_pkthdr.rcvif = ifp;
+				int cap = rbd->rbd_jumbo ? ET_JUMBO_FRAMELEN :
+				    (ring_idx == 0 ? MHLEN : MCLBYTES);
 
-				m_adj(m, -ETHER_CRC_LEN);
-
-				IFNET_STAT_INC(ifp, ipackets, 1);
-				ifp->if_input(ifp, m, NULL, -1);
+				if (buflen > cap) {
+					/*
+					 * The device-supplied length runs
+					 * past the buffer backing this
+					 * descriptor; m_len must never
+					 * exceed the backing store
+					 * (DF-4055: heap OOB read handed
+					 * to if_input/BPF, then
+					 * m_free "overflowed mbuf").
+					 */
+					m_freem(m);
+					IFNET_STAT_INC(ifp, ierrors, 1);
+				} else {
+					m->m_pkthdr.len = m->m_len = buflen;
+					m->m_pkthdr.rcvif = ifp;
+
+					m_adj(m, -ETHER_CRC_LEN);
+
+					IFNET_STAT_INC(ifp, ipackets, 1);
+					ifp->if_input(ifp, m, NULL, -1);
+				}
 			}
 		} else {
 			IFNET_STAT_INC(ifp, ierrors, 1);
@@ -2026,6 +2056,19 @@
 	end = __SHIFTOUT(tx_done, ET_TX_DONE_POS_INDEX);
 	wrap = (tx_done & ET_TX_DONE_POS_WRAP) ? 1 : 0;
 
+	/*
+	 * The DMA'd done index field (ET_TX_DONE_POS_INDEX) is 10 bits
+	 * wide while the TX ring has ET_TX_NDESC (512) descriptors.  An
+	 * out-of-range value can never equal tbd_start_index and would
+	 * spin the completion walk until tbd_used underflows its
+	 * KKASSERT (DF-4056).
+	 */
+	if (end >= ET_TX_NDESC) {
+		IFNET_STAT_INC(ifp, oerrors, 1);
+		if_printf(ifp, "invalid TX done index %d\n", end);
+		return;
+	}
+
 	while (tbd->tbd_start_index != end || tbd->tbd_start_wrap != wrap) {
 		struct et_txbuf *tb;
 
@@ -2067,7 +2110,18 @@
 	struct ifnet *ifp = &sc->arpcom.ac_if;
 	struct mii_data *mii = device_get_softc(sc->sc_miibus);
 
-	lwkt_serialize_enter(ifp->if_serializer);
+	/*
+	 * Never block on the serializer here: et_stop() stops this
+	 * callout synchronously while holding that serializer, so a
+	 * blocking acquire deadlocks the two forever (AB-BA, DF-4057;
+	 * et_stop's callout_stop waits "costp" while this tick waits
+	 * for the serializer).  Skip the tick and retry on the next
+	 * one instead.
+	 */
+	if (!lwkt_serialize_try(ifp->if_serializer)) {
+		callout_reset(&sc->sc_tick, hz, et_tick, sc);
+		return;
+	}
 
 	mii_tick(mii);
 	if ((sc->sc_flags & ET_FLAG_TXRX_ENABLED) == 0 &&
@@ -2077,7 +2131,10 @@
 		if (et_enable_txrx(sc, 0) == 0)
 			if_devstart(ifp);
 	}
-	callout_reset(&sc->sc_tick, hz, et_tick, sc);
+	/* Re-arm only while running: et_stop() clears IFF_RUNNING
+	 * before it could be re-armed under it (DF-4057). */
+	if (ifp->if_flags & IFF_RUNNING)
+		callout_reset(&sc->sc_tick, hz, et_tick, sc);
 
 	lwkt_serialize_exit(ifp->if_serializer);
 }