⬢ DragonFlyBSD Kernel Audit
DF-4325 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/netif/sf/if_sf.c
+++ b/sys/dev/netif/sf/if_sf.c
@@ -992,6 +992,7 @@ sf_txeof(struct sf_softc *sc)
 	int			txcons, cmpprodidx, cmpconsidx;
 	struct sf_tx_cmpdesc_type1 *cur_cmp;
 	struct sf_tx_bufdesc_type0 *cur_tx;
+	int			idx;
 	struct ifnet		*ifp;
 
 	ifp = &sc->arpcom.ac_if;
@@ -1002,10 +1003,19 @@ sf_txeof(struct sf_softc *sc)
 	while (cmpconsidx != cmpprodidx) {
 		cur_cmp = &sc->sf_ldata->sf_tx_clist[cmpconsidx];
-		cur_tx = &sc->sf_ldata->sf_tx_dlist[cur_cmp->sf_index >> 7];
+
+		/*
+		 * The completion descriptor's sf_index is a 15-bit
+		 * byte offset DMA-written by the NIC; >> 7 it must
+		 * index the 128-entry TX descriptor ring.  A NIC that
+		 * supplies an offset >= SF_TX_DLIST_SIZE (16384) would
+		 * otherwise index far past the ring, feeding an OOB
+		 * "mbuf pointer" to m_freem() and underflowing
+		 * sf_tx_cnt.  Reject and resync instead.
+		 */
+		idx = cur_cmp->sf_index >> 7;
+		if (idx >= SF_TX_DLIST_CNT) {
+			kprintf("sf%d: bogus TX completion index %d, "
+			    "resetting\n", sc->sf_unit, idx);
+			ifp->if_flags &= ~IFF_RUNNING;
+			sf_init(sc);
+			return;
+		}
+		cur_tx = &sc->sf_ldata->sf_tx_dlist[idx];
 
 		if (cur_cmp->sf_txstat & SF_TXSTAT_TX_OK)
 			IFNET_STAT_INC(ifp, opackets, 1);