Index: sys/dev/usb/wlan/if_upgt.c =================================================================== --- sys/dev/usb/wlan/if_upgt.c +++ sys/dev/usb/wlan/if_upgt.c @@ -118,7 +118,7 @@ static struct mbuf * upgt_rxeof(struct usb_xfer *, struct upgt_data *, int *); static struct mbuf * - upgt_rx(struct upgt_softc *, uint8_t *, int, int *); + upgt_rx(struct upgt_softc *, uint8_t *, uint16_t, int *); static void upgt_txeof(struct usb_xfer *, struct upgt_data *); static int upgt_eeprom_read(struct upgt_softc *); static int upgt_eeprom_parse(struct upgt_softc *); @@ -306,7 +306,9 @@ DPRINTF(sc, UPGT_DEBUG_FW, "memory address rx start=0x%08x\n", sc->sc_memaddr_rx_start); - upgt_mem_init(sc); + error = upgt_mem_init(sc); + if (error) + goto fail4; /* Load the firmware. */ error = upgt_fw_load(sc); @@ -1043,7 +1045,7 @@ struct ieee80211com *ic = &sc->sc_ic; struct upgt_eeprom_header *eeprom_header; struct upgt_eeprom_option *eeprom_option; - uint16_t option_len; + uint32_t option_len; uint16_t option_type; uint16_t preamble_len; int option_end = 0; @@ -1058,7 +1060,9 @@ /* sanity check */ if (eeprom_option >= (struct upgt_eeprom_option *) - (sc->sc_eeprom + UPGT_EEPROM_SIZE)) { + (sc->sc_eeprom + UPGT_EEPROM_SIZE) || + eeprom_option < (struct upgt_eeprom_option *) + sc->sc_eeprom) { return (EINVAL); } @@ -1345,7 +1349,7 @@ struct upgt_lmac_header *header; struct upgt_lmac_eeprom *eeprom; uint8_t h1_type; - uint16_t h2_type; + uint16_t h2_type, pkglen; int actlen, sumlen; usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); @@ -1378,13 +1382,22 @@ eeprom = (struct upgt_lmac_eeprom *)(data->buf + 4); uint16_t eeprom_offset = le16toh(eeprom->offset); uint16_t eeprom_len = le16toh(eeprom->len); + int buf_offset = sizeof(struct upgt_lmac_eeprom) + 4; + if (eeprom_len > actlen - buf_offset || + eeprom_len > sizeof(sc->sc_eeprom) - eeprom_offset) { + device_printf(sc->sc_dev, + "%s: wrong eeprom length (%u) / offset (%u)!\n", + __func__, eeprom_len, eeprom_offset); + return (NULL); + } + DPRINTF(sc, UPGT_DEBUG_FW, "received EEPROM block (offset=%d, len=%d)\n", eeprom_offset, eeprom_len); memcpy(sc->sc_eeprom + eeprom_offset, - data->buf + sizeof(struct upgt_lmac_eeprom) + 4, + data->buf + buf_offset, eeprom_len); /* EEPROM data has arrived in time, wakeup. */ @@ -1398,8 +1411,16 @@ h1_type == UPGT_H1_TYPE_RX_DATA_MGMT) { DPRINTF(sc, UPGT_DEBUG_RECV, "%s: received 802.11 RX data\n", __func__); - m = upgt_rx(sc, data->buf + 4, le16toh(header->header1.len), - rssi); + + pkglen = le16toh(header->header1.len); + if (actlen - 4 < pkglen) { + device_printf(sc->sc_dev, + "%s: actlen - 4 (%d) < pkglen (%d)!\n", + __func__, actlen - 4, pkglen); + return (NULL); + } + + m = upgt_rx(sc, data->buf + 4, pkglen, rssi); } else if (h1_type == UPGT_H1_TYPE_CTRL && h2_type == UPGT_H2_TYPE_STATS) { DPRINTF(sc, UPGT_DEBUG_STAT, "%s: received statistic data\n", @@ -1433,7 +1454,7 @@ } static struct mbuf * -upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen, int *rssi) +upgt_rx(struct upgt_softc *sc, uint8_t *data, uint16_t pkglen, int *rssi) { struct ieee80211com *ic = &sc->sc_ic; struct upgt_lmac_rx_desc *rxdesc; @@ -1449,9 +1470,21 @@ /* access RX packet descriptor */ rxdesc = (struct upgt_lmac_rx_desc *)data; + if (pkglen < IEEE80211_ACK_LEN) { + device_printf(sc->sc_dev, + "%s: frame is too short! (pkglen %d)\n", + __func__, pkglen); + return (NULL); + } + + if (pkglen >= MCLBYTES - ETHER_ALIGN) { + device_printf(sc->sc_dev, + "%s: A current mbuf storage is small (%d)\n", + __func__, pkglen + ETHER_ALIGN); + return (NULL); + } + /* create mbuf which is suitable for strict alignment archs */ - KASSERT((pkglen + ETHER_ALIGN) < MCLBYTES, - ("A current mbuf storage is small (%d)", pkglen + ETHER_ALIGN)); m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { device_printf(sc->sc_dev, "could not create RX mbuf\n"); @@ -1701,6 +1734,15 @@ { int i; + if ((uint64_t)(sc->sc_memaddr_frame_start + MCLBYTES) > + sc->sc_memaddr_frame_end) { + device_printf(sc->sc_dev, + "%s: start (%u) + MCLBYTES > end (%u)!\n", + __func__, sc->sc_memaddr_frame_start, + sc->sc_memaddr_frame_end); + return (EINVAL); + } + for (i = 0; i < UPGT_MEMORY_MAX_PAGES; i++) { sc->sc_memory.page[i].used = 0; @@ -1716,6 +1758,14 @@ sc->sc_memory.page[i - 1].addr + MCLBYTES; } + /* Memory wrap check. */ + if (sc->sc_memaddr_frame_start > sc->sc_memory.page[i].addr) { + device_printf(sc->sc_dev, + "%s: frame_start (%u) is too large!\n", + __func__, sc->sc_memaddr_frame_start); + return (EINVAL); + } + if (sc->sc_memory.page[i].addr + MCLBYTES >= sc->sc_memaddr_frame_end) break; @@ -1753,17 +1803,19 @@ /* * Seek to beginning of Boot Record Area (BRA). */ - for (offset = 0; offset < fw->datasize; offset += sizeof(*uc)) { + for (offset = 0; + offset + sizeof(*uc) <= fw->datasize; + offset += sizeof(*uc)) { uc = (const uint32_t *)((const uint8_t *)fw->data + offset); if (*uc == 0) break; } - for (; offset < fw->datasize; offset += sizeof(*uc)) { + for (; offset + sizeof(*uc) <= fw->datasize; offset += sizeof(*uc)) { uc = (const uint32_t *)((const uint8_t *)fw->data + offset); if (*uc != 0) break; } - if (offset == fw->datasize) { + if (offset + sizeof(*uc) > fw->datasize) { device_printf(sc->sc_dev, "firmware Boot Record Area not found\n"); error = EIO; @@ -1776,13 +1828,21 @@ /* * Parse Boot Record Area (BRA) options. */ - while (offset < fw->datasize && bra_end == 0) { + while (offset <= fw->datasize - sizeof(*bra_opt) && bra_end == 0) { /* get current BRA option */ p = (const uint8_t *)fw->data + offset; bra_opt = (const struct upgt_fw_bra_option *)p; bra_option_type = le32toh(bra_opt->type); bra_option_len = le32toh(bra_opt->len) * sizeof(*uc); + if (bra_option_len > (size_t)(fw->datasize - offset - + sizeof(*bra_opt)) || + bra_option_len / sizeof(*uc) != le32toh(bra_opt->len)) { + device_printf(sc->sc_dev, "bra_option_len overflow\n"); + error = EINVAL; + goto fail; + } + switch (bra_option_type) { case UPGT_BRA_TYPE_FW: DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_FW len=%d\n", @@ -1821,6 +1881,18 @@ "UPGT_BRA_TYPE_EXPIF len=%d\n", bra_option_len); break; case UPGT_BRA_TYPE_DESCR: + if (sizeof(*descr) > fw->datasize - offset - + sizeof(*bra_opt) || + sizeof(*descr) > bra_option_len) { + device_printf(sc->sc_dev, "%s: no place for" + "'struct upgt_fw_bra_descr' (%zu / %u)\n", + __func__, + fw->datasize - offset - sizeof(*bra_opt), + bra_option_len); + error = EIO; + goto fail; + } + DPRINTF(sc, UPGT_DEBUG_FW, "UPGT_BRA_TYPE_DESCR len=%d\n", bra_option_len); @@ -2111,14 +2183,28 @@ struct upgt_data *data) { struct ieee80211vap *vap = ni->ni_vap; - int error = 0, len; + int error = 0; struct ieee80211_frame *wh; struct ieee80211_key *k; struct upgt_lmac_mem *mem; struct upgt_lmac_tx_desc *txdesc; + uint32_t len; UPGT_ASSERT_LOCKED(sc); + /* Calculate frame size. */ + len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len; + /* We need to align the frame to a 4 byte boundary. */ + len = (len + 3) & ~3; + + if (len > MCLBYTES) { + device_printf(sc->sc_dev, + "%s: the frame %p is too big (%d)\n", + __func__, m, m->m_pkthdr.len); + error = EINVAL; + goto done; + } + upgt_set_led(sc, UPGT_LED_BLINK); /* @@ -2175,19 +2261,14 @@ /* copy frame below our TX descriptor header */ m_copydata(m, 0, m->m_pkthdr.len, data->buf + (sizeof(*mem) + sizeof(*txdesc))); - /* calculate frame size */ - len = sizeof(*mem) + sizeof(*txdesc) + m->m_pkthdr.len; - /* we need to align the frame to a 4 byte boundary */ - len = (len + 3) & ~3; /* calculate frame checksum */ mem->chksum = upgt_chksum_le((uint32_t *)txdesc, len - sizeof(*mem)); data->ni = ni; data->m = m; data->buflen = len; - DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: TX start data sending (%d bytes)\n", + DPRINTF(sc, UPGT_DEBUG_XMIT, "%s: TX start data sending (%u bytes)\n", __func__, len); - KASSERT(len <= MCLBYTES, ("mbuf is small for saving data")); upgt_bulk_tx(sc, data); done: