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
This commit is contained in:
parent
6baf1e4803
commit
fb10d4ee38
@ -2824,69 +2824,80 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen)
|
|||||||
uint8_t ant, rssi;
|
uint8_t ant, rssi;
|
||||||
int8_t nf;
|
int8_t nf;
|
||||||
|
|
||||||
rxwi = mtod(m, struct rt2860_rxwi *);
|
|
||||||
len = le16toh(rxwi->len) & 0xfff;
|
|
||||||
rxwisize = sizeof(struct rt2860_rxwi);
|
rxwisize = sizeof(struct rt2860_rxwi);
|
||||||
if (sc->mac_ver == 0x5592)
|
if (sc->mac_ver == 0x5592)
|
||||||
rxwisize += sizeof(uint64_t);
|
rxwisize += sizeof(uint64_t);
|
||||||
else if (sc->mac_ver == 0x3593)
|
else if (sc->mac_ver == 0x3593)
|
||||||
rxwisize += sizeof(uint32_t);
|
rxwisize += sizeof(uint32_t);
|
||||||
if (__predict_false(len > dmalen)) {
|
|
||||||
m_freem(m);
|
if (__predict_false(dmalen <
|
||||||
counter_u64_add(ic->ic_ierrors, 1);
|
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,
|
RUN_DPRINTF(sc, RUN_DEBUG_RECV,
|
||||||
"bad RXWI length %u > %u\n", len, dmalen);
|
"bad RXWI length %u > %u\n", len, dmalen);
|
||||||
return;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Rx descriptor is located at the end */
|
/* Rx descriptor is located at the end */
|
||||||
rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen);
|
rxd = (struct rt2870_rxd *)(mtod(m, caddr_t) + dmalen);
|
||||||
flags = le32toh(rxd->flags);
|
flags = le32toh(rxd->flags);
|
||||||
|
|
||||||
if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) {
|
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",
|
RUN_DPRINTF(sc, RUN_DEBUG_RECV, "%s error.\n",
|
||||||
(flags & RT2860_RX_CRCERR)?"CRC":"ICV");
|
(flags & RT2860_RX_CRCERR)?"CRC":"ICV");
|
||||||
return;
|
goto fail;
|
||||||
}
|
|
||||||
|
|
||||||
m->m_data += rxwisize;
|
|
||||||
m->m_pkthdr.len = m->m_len -= rxwisize;
|
|
||||||
|
|
||||||
wh = mtod(m, struct ieee80211_frame *);
|
|
||||||
|
|
||||||
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) {
|
if (flags & RT2860_RX_L2PAD) {
|
||||||
|
/*
|
||||||
|
* XXX OpenBSD removes padding between header
|
||||||
|
* and payload here...
|
||||||
|
*/
|
||||||
RUN_DPRINTF(sc, RUN_DEBUG_RECV,
|
RUN_DPRINTF(sc, RUN_DEBUG_RECV,
|
||||||
"received RT2860_RX_L2PAD frame\n");
|
"received RT2860_RX_L2PAD frame\n");
|
||||||
len += 2;
|
len += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
ni = ieee80211_find_rxnode(ic,
|
m->m_data += rxwisize;
|
||||||
mtod(m, struct ieee80211_frame_min *));
|
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 (len >= sizeof(struct ieee80211_frame_min)) {
|
||||||
|
ni = ieee80211_find_rxnode(ic,
|
||||||
|
mtod(m, struct ieee80211_frame_min *));
|
||||||
|
} else
|
||||||
|
ni = NULL;
|
||||||
|
|
||||||
if (__predict_false(flags & RT2860_RX_MICERR)) {
|
if (__predict_false(flags & RT2860_RX_MICERR)) {
|
||||||
/* report MIC failures to net80211 for TKIP */
|
/* report MIC failures to net80211 for TKIP */
|
||||||
if (ni != NULL)
|
if (ni != NULL)
|
||||||
ieee80211_notify_michael_failure(ni->ni_vap, wh,
|
ieee80211_notify_michael_failure(ni->ni_vap, wh,
|
||||||
rxwi->keyidx);
|
rxwi->keyidx);
|
||||||
m_freem(m);
|
|
||||||
counter_u64_add(ic->ic_ierrors, 1);
|
|
||||||
RUN_DPRINTF(sc, RUN_DEBUG_RECV,
|
RUN_DPRINTF(sc, RUN_DEBUG_RECV,
|
||||||
"MIC error. Someone is lying.\n");
|
"MIC error. Someone is lying.\n");
|
||||||
return;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ant = run_maxrssi_chain(sc, rxwi);
|
ant = run_maxrssi_chain(sc, rxwi);
|
||||||
rssi = rxwi->rssi[ant];
|
rssi = rxwi->rssi[ant];
|
||||||
nf = run_rssi2dbm(sc, rssi, ant);
|
nf = run_rssi2dbm(sc, rssi, ant);
|
||||||
|
|
||||||
m->m_pkthdr.len = m->m_len = len;
|
|
||||||
|
|
||||||
if (__predict_false(ieee80211_radiotap_active(ic))) {
|
if (__predict_false(ieee80211_radiotap_active(ic))) {
|
||||||
struct run_rx_radiotap_header *tap = &sc->sc_rxtap;
|
struct run_rx_radiotap_header *tap = &sc->sc_rxtap;
|
||||||
uint16_t phy;
|
uint16_t phy;
|
||||||
@ -2934,6 +2945,12 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen)
|
|||||||
} else {
|
} else {
|
||||||
(void)ieee80211_input_all(ic, m, rssi, nf);
|
(void)ieee80211_input_all(ic, m, rssi, nf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
m_freem(m);
|
||||||
|
counter_u64_add(ic->ic_ierrors, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -2943,7 +2960,7 @@ run_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error)
|
|||||||
struct ieee80211com *ic = &sc->sc_ic;
|
struct ieee80211com *ic = &sc->sc_ic;
|
||||||
struct mbuf *m = NULL;
|
struct mbuf *m = NULL;
|
||||||
struct mbuf *m0;
|
struct mbuf *m0;
|
||||||
uint32_t dmalen;
|
uint32_t dmalen, mbuf_len;
|
||||||
uint16_t rxwisize;
|
uint16_t rxwisize;
|
||||||
int xferlen;
|
int xferlen;
|
||||||
|
|
||||||
@ -3049,6 +3066,14 @@ tr_setup:
|
|||||||
break;
|
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 */
|
/* copy aggregated frames to another mbuf */
|
||||||
m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
|
m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
|
||||||
if (__predict_false(m0 == NULL)) {
|
if (__predict_false(m0 == NULL)) {
|
||||||
@ -3058,14 +3083,13 @@ tr_setup:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_copydata(m, 4 /* skip 32-bit DMA-len header */,
|
m_copydata(m, 4 /* skip 32-bit DMA-len header */,
|
||||||
dmalen + sizeof(struct rt2870_rxd), mtod(m0, caddr_t));
|
mbuf_len, mtod(m0, caddr_t));
|
||||||
m0->m_pkthdr.len = m0->m_len =
|
m0->m_pkthdr.len = m0->m_len = mbuf_len;
|
||||||
dmalen + sizeof(struct rt2870_rxd);
|
|
||||||
run_rx_frame(sc, m0, dmalen);
|
run_rx_frame(sc, m0, dmalen);
|
||||||
|
|
||||||
/* update data ptr */
|
/* update data ptr */
|
||||||
m->m_data += dmalen + 8;
|
m->m_data += mbuf_len + 4;
|
||||||
m->m_pkthdr.len = m->m_len -= dmalen + 8;
|
m->m_pkthdr.len = m->m_len -= mbuf_len + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* make sure we free the source buffer, if any */
|
/* make sure we free the source buffer, if any */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user