diff options
author | Jerry Lee <jerrylee@broadcom.com> | 2016-05-24 20:12:20 -0700 |
---|---|---|
committer | Ecco Park <eccopark@google.com> | 2016-05-25 18:24:11 +0000 |
commit | 094303210a20c0fe6f00853ca313857312471668 (patch) | |
tree | 26d4757a8f536989e6a44828abc4b4a9b4c84204 | |
parent | edbe4c67fc0f0914a91fa64fb48d4e7e069d949b (diff) |
net: wireless: bcmdhd: security enhancement for BRCM ether typeandroid-6.0.1_r0.92
strict ckeckup for type and event data length
Required FW for bcm4356/bcm4354 >= 7.35.101.3 (CL/603540)
BUG=26492805
Change-Id: I64c2b90d6747f8966495363c2424d6b18f9fb7cb
Signed-off-by: Jerry Lee <jerrylee@broadcom.com>
-rw-r--r-- | drivers/net/wireless/bcmdhd/bcmevent.c | 120 | ||||
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd_common.c | 89 | ||||
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd_linux.c | 26 | ||||
-rw-r--r-- | drivers/net/wireless/bcmdhd/include/proto/bcmevent.h | 15 |
5 files changed, 199 insertions, 52 deletions
diff --git a/drivers/net/wireless/bcmdhd/bcmevent.c b/drivers/net/wireless/bcmdhd/bcmevent.c index c7a902d7c60e..b85f111bce18 100644 --- a/drivers/net/wireless/bcmdhd/bcmevent.c +++ b/drivers/net/wireless/bcmdhd/bcmevent.c @@ -25,9 +25,12 @@ #include <typedefs.h> #include <bcmutils.h> +#include <bcmendian.h> #include <proto/ethernet.h> #include <proto/bcmeth.h> #include <proto/bcmevent.h> +#include <proto/dnglevent.h> +#include <proto/802.11.h> /* Table of event name strings for UIs and debugging dumps */ @@ -192,3 +195,120 @@ const char *bcmevent_get_name(uint event_type) */ return ((event_name) ? event_name : "Unknown Event"); } + +/* + * Validate if the event is proper and if valid copy event header to event. + * If proper event pointer is passed, to just validate, pass NULL to event. + * + * Return values are + * BCME_OK - It is a BRCM event or BRCM dongle event + * BCME_NOTFOUND - Not BRCM, not an event, may be okay + * BCME_BADLEN - Bad length, should not process, just drop + */ +int +is_wlc_event_frame(void *pktdata, uint pktlen, uint16 exp_usr_subtype, + bcm_event_msg_u_t *out_event) +{ + uint16 len; + uint16 subtype; + uint16 usr_subtype; + bcm_event_t *bcm_event; + uint8 *pktend; + int err = BCME_OK; + + pktend = (uint8 *)pktdata + pktlen; + bcm_event = (bcm_event_t *)pktdata; + + /* only care about 16-bit subtype / length versions */ + if ((uint8 *)&bcm_event->bcm_hdr < pktend) { + uint8 short_subtype = *(uint8 *)&bcm_event->bcm_hdr; + if (!(short_subtype & 0x80)) { + err = BCME_NOTFOUND; + goto done; + } + } + + /* must have both ether_header and bcmeth_hdr */ + if (pktlen < OFFSETOF(bcm_event_t, event)) { + err = BCME_BADLEN; + goto done; + } + + /* check length in bcmeth_hdr */ + len = ntoh16_ua((void *)&bcm_event->bcm_hdr.length); + if (((uint8 *)&bcm_event->bcm_hdr.version + len) > pktend) { + err = BCME_BADLEN; + goto done; + } + + /* match on subtype, oui and usr subtype for BRCM events */ + subtype = ntoh16_ua((void *)&bcm_event->bcm_hdr.subtype); + if (subtype != BCMILCP_SUBTYPE_VENDOR_LONG) { + err = BCME_NOTFOUND; + goto done; + } + + if (bcmp(BRCM_OUI, &bcm_event->bcm_hdr.oui[0], DOT11_OUI_LEN)) { + err = BCME_NOTFOUND; + goto done; + } + + /* if it is a bcm_event or bcm_dngl_event_t, validate it */ + usr_subtype = ntoh16_ua((void *)&bcm_event->bcm_hdr.usr_subtype); + switch (usr_subtype) { + case BCMILCP_BCM_SUBTYPE_EVENT: + if (pktlen < sizeof(bcm_event_t)) { + err = BCME_BADLEN; + goto done; + } + + len = sizeof(bcm_event_t) + ntoh32_ua((void *)&bcm_event->event.datalen); + if ((uint8 *)pktdata + len > pktend) { + err = BCME_BADLEN; + goto done; + } + + if (exp_usr_subtype && (exp_usr_subtype != usr_subtype)) { + err = BCME_NOTFOUND; + goto done; + } + + if (out_event) { + /* ensure BRCM event pkt aligned */ + memcpy(&out_event->event, &bcm_event->event, sizeof(wl_event_msg_t)); + } + + break; + case BCMILCP_BCM_SUBTYPE_DNGLEVENT: + if (pktlen < sizeof(bcm_dngl_event_t)) { + err = BCME_BADLEN; + goto done; + } + + len = sizeof(bcm_dngl_event_t) + + ntoh16_ua((void *)&((bcm_dngl_event_t *)pktdata)->dngl_event.datalen); + if ((uint8 *)pktdata + len > pktend) { + err = BCME_BADLEN; + goto done; + } + + if (exp_usr_subtype && (exp_usr_subtype != usr_subtype)) { + err = BCME_NOTFOUND; + goto done; + } + + if (out_event) { + /* ensure BRCM dngl event pkt aligned */ + memcpy(&out_event->dngl_event, &((bcm_dngl_event_t *)pktdata)->dngl_event, + sizeof(bcm_dngl_event_msg_t)); + } + + break; + default: + err = BCME_NOTFOUND; + goto done; + } + +done: + return err; +} diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index f2cd8e646124..59e3148fa79d 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -812,6 +812,7 @@ extern int net_os_send_hang_message(struct net_device *dev); extern int wl_host_event(dhd_pub_t *dhd_pub, int *idx, void *pktdata, size_t pktlen, wl_event_msg_t *, void **data_ptr, void *); extern void wl_event_to_host_order(wl_event_msg_t * evt); +extern int wl_host_event_get_data(void *pktdata, uint pktlen, bcm_event_msg_u_t *evu); extern int dhd_wl_ioctl(dhd_pub_t *dhd_pub, int ifindex, wl_ioctl_t *ioc, void *buf, int len); extern int dhd_wl_ioctl_cmd(dhd_pub_t *dhd_pub, int cmd, void *arg, int len, uint8 set, diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 235c4019e4ec..fc601d37263b 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -107,8 +107,11 @@ extern int dhd_change_mtu(dhd_pub_t *dhd, int new_mtu, int ifidx); extern int dhd_get_concurrent_capabilites(dhd_pub_t *dhd); #endif extern int dhd_socram_dump(struct dhd_bus *bus); -static void dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event, size_t pktlen); -static int dngl_host_event(dhd_pub_t *dhdp, void *pktdata, size_t pktlen); +static void dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event, + bcm_dngl_event_msg_t *dngl_event, size_t pktlen); +static int dngl_host_event(dhd_pub_t *dhdp, void *pktdata, bcm_dngl_event_msg_t *dngl_event, + size_t pktlen); + bool ap_cfg_running = FALSE; bool ap_fw_loaded = FALSE; @@ -1377,27 +1380,18 @@ wl_show_host_event(dhd_pub_t *dhd_pub, wl_event_msg_t *event, void *event_data, /* Check whether packet is a BRCM dngl event pkt. If it is, process event data. */ int -dngl_host_event(dhd_pub_t *dhdp, void *pktdata, size_t pktlen) +dngl_host_event(dhd_pub_t *dhdp, void *pktdata, bcm_dngl_event_msg_t *dngl_event, size_t pktlen) { bcm_dngl_event_t *pvt_data = (bcm_dngl_event_t *)pktdata; - if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) { - DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__)); - return BCME_ERROR; - } - /* Check to see if this is a DNGL event */ - if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) == - BCMILCP_BCM_SUBTYPE_DNGLEVENT) { - dngl_host_event_process(dhdp, pvt_data, pktlen); - return BCME_OK; - } - return BCME_ERROR; + dngl_host_event_process(dhdp, pvt_data, dngl_event, pktlen); + return BCME_OK; } void -dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event, size_t pktlen) +dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event, + bcm_dngl_event_msg_t *dngl_event, size_t pktlen) { - bcm_dngl_event_msg_t *dngl_event = &event->dngl_event; uint8 *p = (uint8 *)(event + 1); uint16 type = ntoh16_ua((void *)&dngl_event->event_type); uint16 datalen = ntoh16_ua((void *)&dngl_event->datalen); @@ -1497,6 +1491,21 @@ dngl_host_event_process(dhd_pub_t *dhdp, bcm_dngl_event_t *event, size_t pktlen) break; } } + +/* Check whether packet is a BRCM event pkt. If it is, record event data. */ +int wl_host_event_get_data(void *pktdata, uint pktlen, bcm_event_msg_u_t *evu) +{ + int ret; + + ret = is_wlc_event_frame(pktdata, pktlen, 0, evu); + if (ret != BCME_OK) { + DHD_ERROR(("%s: Invalid event frame, err = %d\n", + __FUNCTION__, ret)); + } + + return ret; +} + int wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, size_t pktlen, wl_event_msg_t *event, void **data_ptr, void *raw_event) { @@ -1507,45 +1516,41 @@ int wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, size_t pktlen, uint16 flags; int evlen; int hostidx; + int ret; + uint16 usr_subtype; + bcm_event_msg_u_t evu; - /* If it is a DNGL event process it first */ - if (dngl_host_event(dhd_pub, pktdata, pktlen) == BCME_OK) { - return BCME_OK; - } - - if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) { - DHD_ERROR(("%s: mismatched OUI, bailing\n", __FUNCTION__)); - return (BCME_ERROR); + ret = wl_host_event_get_data(pktdata, pktlen, &evu); + if (ret != BCME_OK) { + return ret; } - /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */ - if (ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype) != BCMILCP_BCM_SUBTYPE_EVENT) { - DHD_ERROR(("%s: mismatched subtype, bailing\n", __FUNCTION__)); - return (BCME_ERROR); - } + usr_subtype = ntoh16_ua((void *)&pvt_data->bcm_hdr.usr_subtype); + switch (usr_subtype) { + case BCMILCP_BCM_SUBTYPE_EVENT: + memcpy(event, &evu.event, sizeof(wl_event_msg_t)); + *data_ptr = &pvt_data[1]; + break; - if (pktlen < sizeof(bcm_event_t)) - return (BCME_ERROR); + case BCMILCP_BCM_SUBTYPE_DNGLEVENT: + /* If it is a DNGL event process it first */ + dngl_host_event(dhd_pub, pktdata, &evu.dngl_event, pktlen); - *data_ptr = &pvt_data[1]; - event_data = *data_ptr; + /* Return error purposely to prevent DNGL event being processed as BRCM event */ + return BCME_ERROR; + default: + return BCME_NOTFOUND; + } - /* memcpy since BRCM event pkt may be unaligned. */ - memcpy(event, &pvt_data->event, sizeof(wl_event_msg_t)); + /* start wl_event_msg process */ + event_data = *data_ptr; type = ntoh32_ua((void *)&event->event_type); flags = ntoh16_ua((void *)&event->flags); status = ntoh32_ua((void *)&event->status); - datalen = ntoh32_ua((void *)&event->datalen); - if (datalen > pktlen) - return (BCME_ERROR); - evlen = datalen + sizeof(bcm_event_t); - if (evlen > pktlen) { - return BCME_ERROR; - } /* find equivalent host index for event ifidx */ hostidx = dhd_ifidx2hostidx(dhd_pub->info, event->ifidx); diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index c2f3e7e79a45..9984b2ae813a 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -3021,16 +3021,23 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan, /* Process special event packets and then discard them */ memset(&event, 0, sizeof(event)); if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) { - dhd_wl_host_event(dhd, &ifidx, + int ret_event; + + ret_event = dhd_wl_host_event(dhd, &ifidx, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) skb_mac_header(skb), #else skb->mac.raw, #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */ - len - 2, + len, &event, &data); + if (ret_event != BCME_OK) { + PKTFREE(dhdp->osh, pktbuf, FALSE); + continue; + } + wl_event_to_host_order(&event); if (!tout_ctrl) tout_ctrl = DHD_PACKET_TIMEOUT_MS; @@ -7447,11 +7454,11 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, size_t pktlen, ASSERT(dhd != NULL); #ifdef SHOW_LOGTRACE - bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, pktlen, - event, data, &dhd->event_data); + bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, pktlen, + event, data, &dhd->event_data); #else - bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, pktlen, - event, data, NULL); + bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, pktlen, + event, data, NULL); #endif /* SHOW_LOGTRACE */ if (bcmerror != BCME_OK) @@ -7462,12 +7469,11 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, size_t pktlen, /* * Wireless ext is on primary interface only */ - - ASSERT(dhd->iflist[*ifidx] != NULL); - ASSERT(dhd->iflist[*ifidx]->net != NULL); + ASSERT(dhd->iflist[*ifidx] != NULL); + ASSERT(dhd->iflist[*ifidx]->net != NULL); if (dhd->iflist[*ifidx]->net) { - wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); + wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); } } #endif /* defined(WL_WIRELESS_EXT) */ diff --git a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h index 56cf83b32eaf..098da15f45f7 100644 --- a/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h +++ b/drivers/net/wireless/bcmdhd/include/proto/bcmevent.h @@ -40,6 +40,7 @@ #endif /* #include <ethernet.h> -- TODO: req., excluded to overwhelming coupling (break up ethernet.h) */ #include <proto/bcmeth.h> +#include <proto/dnglevent.h> /* This marks the start of a packed structure section. */ #include <packed_section_start.h> @@ -94,6 +95,17 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event { /* data portion follows */ } BWL_POST_PACKED_STRUCT bcm_event_t; +/* + * used by host event + * Note: If additional event types are added, it should come on is_wlc_event_frame() as well. + */ +typedef union bcm_event_msg_u { + wl_event_msg_t event; + bcm_dngl_event_msg_t dngl_event; + + /* add new event here */ +} bcm_event_msg_u_t; + #define BCM_MSG_LEN (sizeof(bcm_event_t) - sizeof(bcmeth_hdr_t) - sizeof(struct ether_header)) /* Event messages */ @@ -234,6 +246,9 @@ typedef BWL_PRE_PACKED_STRUCT struct bcm_event { /* define an API for getting the string name of an event */ extern const char *bcmevent_get_name(uint event_type); +/* validate if the event is proper and if valid copy event header to event */ +extern int is_wlc_event_frame(void *pktdata, uint pktlen, uint16 exp_usr_subtype, + bcm_event_msg_u_t *out_event); /* Event status codes */ |