--- a/sys/netproto/802_11/wlan/ieee80211_rssadapt.c 2026-09-13 07:10:01.797256875 +0000 +++ b/sys/netproto/802_11/wlan/ieee80211_rssadapt.c 2026-09-13 07:10:25.728951317 +0000 @@ -194,6 +194,12 @@ } } else ra = ni->ni_rctls; + if (rs->rs_nrates == 0) { + /* An empty rateset leaves the pick loop below at rix -1, + * which propagates into ra_rix and every threshold access. */ + if_printf(vap->iv_ifp, "%s: empty rateset\n", __func__); + return; + } ra->ra_rs = rsa; ra->ra_rates = *rs; rssadapt_updatestats(ra); @@ -253,6 +259,9 @@ for (rix = rs->rs_nrates-1; rix >= 0; rix--) if ((*thrs)[rix] < (rssi << 8)) break; + if (rix < 0) /* no threshold strictly below rssi<<8: + * rssi==0 (fresh node) or converged */ + rix = 0; if (rix != ra->ra_rix) { /* update public rate */ ni->ni_txrate = ni->ni_rates.rs_rates[rix] & IEEE80211_RATE_VAL; @@ -281,6 +290,11 @@ thrs = &ra->ra_rate_thresh[bucket(pktlen)]; rix = ra->ra_rix; + if ((int)rix < 0) { + /* rix is u_int: -1 becomes 0xffffffff and (*thrs)[rix] + * would access ~+8GiB past the chunk. */ + return; + } last_thr = (*thrs)[rix]; (*thrs)[rix] = interpolate(master_expavgctl.rc_thresh, last_thr, (rssi << 8));