DF-4055 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | 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); } |