Author: avos
Date: Sun Jan 13 06:01:36 2019
New Revision: 342991
URL: https://svnweb.freebsd.org/changeset/base/342991

Log:
  net80211: provide rate validation for injected frames.
  
  There may be various side effects (device timeout, firmware and / or
  kernel panic) when an invalid (or inapplicable - e.g., an MCS rate
  for 11g-only device) is set; check rates before sending the frame to
  the driver.
  
  How-to-reproduce:
  Set an MCS (real or bogus - with 0x80 bit set) rate in ibp_rate0 field
  for any device that uses ieee80211_isratevalid() for rate checks -
  rum(4), run(4), ural(4), bwi(4) or ral(4); if kernel is compiled
  with INVARIANTS the check will result in "rate %d is basic/mcs?" panic.
  
  Tested with WUSB54GC (rum(4)), AP mode.
  
  MFC after:    1 week

Modified:
  head/sys/net80211/ieee80211_output.c

Modified: head/sys/net80211/ieee80211_output.c
==============================================================================
--- head/sys/net80211/ieee80211_output.c        Sun Jan 13 05:31:53 2019        
(r342990)
+++ head/sys/net80211/ieee80211_output.c        Sun Jan 13 06:01:36 2019        
(r342991)
@@ -604,6 +604,97 @@ ieee80211_validate_frame(struct mbuf *m,
        return (0);
 }
 
+static int
+ieee80211_validate_rate(struct ieee80211_node *ni, uint8_t rate)
+{
+       struct ieee80211com *ic = ni->ni_ic;
+
+       if (IEEE80211_IS_HT_RATE(rate)) {
+               if ((ic->ic_htcaps & IEEE80211_HTC_HT) == 0)
+                       return (EINVAL);
+
+               rate = IEEE80211_RV(rate);
+               if (rate <= 31) {
+                       if (rate > ic->ic_txstream * 8 - 1)
+                               return (EINVAL);
+
+                       return (0);
+               }
+
+               if (rate == 32) {
+                       if ((ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0)
+                               return (EINVAL);
+
+                       return (0);
+               }
+
+               if ((ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) == 0)
+                       return (EINVAL);
+
+               switch (ic->ic_txstream) {
+               case 0:
+               case 1:
+                       return (EINVAL);
+               case 2:
+                       if (rate > 38)
+                               return (EINVAL);
+
+                       return (0);
+               case 3:
+                       if (rate > 52)
+                               return (EINVAL);
+
+                       return (0);
+               case 4:
+               default:
+                       if (rate > 76)
+                               return (EINVAL);
+
+                       return (0);
+               }
+       }
+
+       if (!ieee80211_isratevalid(ic->ic_rt, rate))
+               return (EINVAL);
+
+       return (0);
+}
+
+static int
+ieee80211_sanitize_rates(struct ieee80211_node *ni, struct mbuf *m,
+    const struct ieee80211_bpf_params *params)
+{
+       int error;
+
+       if (!params)
+               return (0);     /* nothing to do */
+
+       /* NB: most drivers assume that ibp_rate0 is set (!= 0). */
+       if (params->ibp_rate0 != 0) {
+               error = ieee80211_validate_rate(ni, params->ibp_rate0);
+               if (error != 0)
+                       return (error);
+       } else {
+               /* XXX pre-setup some default (e.g., mgmt / mcast) rate */
+               /* XXX __DECONST? */
+               (void) m;
+       }
+
+       if (params->ibp_rate1 != 0 &&
+           (error = ieee80211_validate_rate(ni, params->ibp_rate1)) != 0)
+               return (error);
+
+       if (params->ibp_rate2 != 0 &&
+           (error = ieee80211_validate_rate(ni, params->ibp_rate2)) != 0)
+               return (error);
+
+       if (params->ibp_rate3 != 0 &&
+           (error = ieee80211_validate_rate(ni, params->ibp_rate3)) != 0)
+               return (error);
+
+       return (0);
+}
+
 /*
  * 802.11 output routine. This is (currently) used only to
  * connect bpf write calls to the 802.11 layer for injecting
@@ -717,6 +808,10 @@ ieee80211_output(struct ifnet *ifp, struct mbuf *m,
                    m->m_pkthdr.len - ieee80211_hdrsize(wh));
        } else
                M_WME_SETAC(m, WME_AC_BE);
+
+       error = ieee80211_sanitize_rates(ni, m, params);
+       if (error != 0)
+               senderr(error);
 
        IEEE80211_NODE_STAT(ni, tx_data);
        if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to