Author: avos
Date: Mon Jan 21 00:03:35 2019
New Revision: 343234
URL: https://svnweb.freebsd.org/changeset/base/343234

Log:
  run(4): add more length checks in Rx path.
  
  - Discard frames that are bigger than MCLBYTES (to prevent buffer overrun).
  - Check buffer length before accessing its contents.
  - Fix len <-> dmalen check - the last includes Rx Wireless information
  structure size.
  - Fix out-of-bounds read during Rx node search for ACK / CTS frames
  (monitor mode only).
  
  While here:
  - Mark few suspicious places with comments.
  - Move common cleanup to the function end.
  
  MFC after:    1 week

Modified:
  head/sys/dev/usb/wlan/if_run.c

Modified: head/sys/dev/usb/wlan/if_run.c
==============================================================================
--- head/sys/dev/usb/wlan/if_run.c      Sun Jan 20 23:30:16 2019        
(r343233)
+++ head/sys/dev/usb/wlan/if_run.c      Mon Jan 21 00:03:35 2019        
(r343234)
@@ -2824,69 +2824,80 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uin
        uint8_t ant, rssi;
        int8_t nf;
 
-       rxwi = mtod(m, struct rt2860_rxwi *);
-       len = le16toh(rxwi->len) & 0xfff;
        rxwisize = sizeof(struct rt2860_rxwi);
        if (sc->mac_ver == 0x5592)
                rxwisize += sizeof(uint64_t);
        else if (sc->mac_ver == 0x3593)
                rxwisize += sizeof(uint32_t);
-       if (__predict_false(len > dmalen)) {
-               m_freem(m);
-               counter_u64_add(ic->ic_ierrors, 1);
+
+       if (__predict_false(dmalen <
+           rxwisize + sizeof(struct ieee80211_frame_ack))) {
                RUN_DPRINTF(sc, RUN_DEBUG_RECV,
+                   "payload is too short: dma length %u < %zu\n",
+                   dmalen, rxwisize + sizeof(struct ieee80211_frame_ack));
+               goto fail;
+       }
+
+       rxwi = mtod(m, struct rt2860_rxwi *);
+       len = le16toh(rxwi->len) & 0xfff;
+
+       if (__predict_false(len > dmalen - rxwisize)) {
+               RUN_DPRINTF(sc, RUN_DEBUG_RECV,
                    "bad RXWI length %u > %u\n", len, dmalen);
-               return;
+               goto fail;
        }
+
        /* Rx descriptor is located at the end */
        rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen);
        flags = le32toh(rxd->flags);
 
        if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) {
-               m_freem(m);
-               counter_u64_add(ic->ic_ierrors, 1);
                RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s error.\n",
                    (flags & RT2860_RX_CRCERR)?"CRC":"ICV");
-               return;
+               goto fail;
        }
 
+       if (flags & RT2860_RX_L2PAD) {
+               /*
+                * XXX OpenBSD removes padding between header
+                * and payload here...
+                */
+               RUN_DPRINTF(sc, RUN_DEBUG_RECV,
+                   "received RT2860_RX_L2PAD frame\n");
+               len += 2;
+       }
+
        m->m_data += rxwisize;
-       m->m_pkthdr.len = m->m_len -= rxwisize;
+       m->m_pkthdr.len = m->m_len = len;
 
        wh = mtod(m, struct ieee80211_frame *);
 
+       /* XXX wrong for monitor mode */
        if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
                wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED;
                m->m_flags |= M_WEP;
        }
 
-       if (flags & RT2860_RX_L2PAD) {
-               RUN_DPRINTF(sc, RUN_DEBUG_RECV,
-                   "received RT2860_RX_L2PAD frame\n");
-               len += 2;
-       }
+       if (len >= sizeof(struct ieee80211_frame_min)) {
+               ni = ieee80211_find_rxnode(ic,
+                   mtod(m, struct ieee80211_frame_min *));
+       } else
+               ni = NULL;
 
-       ni = ieee80211_find_rxnode(ic,
-           mtod(m, struct ieee80211_frame_min *));
-
        if (__predict_false(flags & RT2860_RX_MICERR)) {
                /* report MIC failures to net80211 for TKIP */
                if (ni != NULL)
                        ieee80211_notify_michael_failure(ni->ni_vap, wh,
                            rxwi->keyidx);
-               m_freem(m);
-               counter_u64_add(ic->ic_ierrors, 1);
                RUN_DPRINTF(sc, RUN_DEBUG_RECV,
                    "MIC error. Someone is lying.\n");
-               return;
+               goto fail;
        }
 
        ant = run_maxrssi_chain(sc, rxwi);
        rssi = rxwi->rssi[ant];
        nf = run_rssi2dbm(sc, rssi, ant);
 
-       m->m_pkthdr.len = m->m_len = len;
-
        if (__predict_false(ieee80211_radiotap_active(ic))) {
                struct run_rx_radiotap_header *tap = &sc->sc_rxtap;
                uint16_t phy;
@@ -2934,6 +2945,12 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uin
        } else {
                (void)ieee80211_input_all(ic, m, rssi, nf);
        }
+
+       return;
+
+fail:
+       m_freem(m);
+       counter_u64_add(ic->ic_ierrors, 1);
 }
 
 static void
@@ -2943,7 +2960,7 @@ run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_
        struct ieee80211com *ic = &sc->sc_ic;
        struct mbuf *m = NULL;
        struct mbuf *m0;
-       uint32_t dmalen;
+       uint32_t dmalen, mbuf_len;
        uint16_t rxwisize;
        int xferlen;
 
@@ -3049,6 +3066,14 @@ tr_setup:
                        break;
                }
 
+               mbuf_len = dmalen + sizeof(struct rt2870_rxd);
+               if (__predict_false(mbuf_len > MCLBYTES)) {
+                       RUN_DPRINTF(sc, RUN_DEBUG_RECV_DESC | RUN_DEBUG_USB,
+                           "payload is too big: mbuf_len %u\n", mbuf_len);
+                       counter_u64_add(ic->ic_ierrors, 1);
+                       break;
+               }
+
                /* copy aggregated frames to another mbuf */
                m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
                if (__predict_false(m0 == NULL)) {
@@ -3058,14 +3083,13 @@ tr_setup:
                        break;
                }
                m_copydata(m, 4 /* skip 32-bit DMA-len header */,
-                   dmalen + sizeof(struct rt2870_rxd), mtod(m0, caddr_t));
-               m0->m_pkthdr.len = m0->m_len =
-                   dmalen + sizeof(struct rt2870_rxd);
+                   mbuf_len, mtod(m0, caddr_t));
+               m0->m_pkthdr.len = m0->m_len = mbuf_len;
                run_rx_frame(sc, m0, dmalen);
 
                /* update data ptr */
-               m->m_data += dmalen + 8;
-               m->m_pkthdr.len = m->m_len -= dmalen + 8;
+               m->m_data += mbuf_len + 4;
+               m->m_pkthdr.len = m->m_len -= mbuf_len + 4;
        }
 
        /* make sure we free the source buffer, if any */
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to