From f66a7ece097f8ac63659862d3e9554fd8c21882d Mon Sep 17 00:00:00 2001 From: Patrick Tjin Date: Wed, 9 Dec 2015 21:17:56 -0800 Subject: net: wireless: bcmdhd: check packet length for event messages Check the datalen field is less than the size of packet received from the network. Signed-off-by: Patrick Tjin Bug: 27335472 [fixes CVE-2016-0802] Signed-off-by: Kees Cook Change-Id: I4fdbbe5218c1d34f928069237382c7f9ecb3b25f --- drivers/net/wireless/bcmdhd/dhd.h | 2 +- drivers/net/wireless/bcmdhd/dhd_common.c | 12 +++++++++++- drivers/net/wireless/bcmdhd/dhd_linux.c | 9 +++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd.h b/drivers/net/wireless/bcmdhd/dhd.h index 60a44b313f3..ba736be6c7e 100644 --- a/drivers/net/wireless/bcmdhd/dhd.h +++ b/drivers/net/wireless/bcmdhd/dhd.h @@ -731,7 +731,7 @@ extern int dhd_ifidx2hostidx(struct dhd_info *dhd, int ifidx); extern int dhd_net2idx(struct dhd_info *dhd, struct net_device *net); extern struct net_device * dhd_idx2net(void *pub, int ifidx); 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, +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); diff --git a/drivers/net/wireless/bcmdhd/dhd_common.c b/drivers/net/wireless/bcmdhd/dhd_common.c index 412c0c79b11..a0ba321eb9d 100644 --- a/drivers/net/wireless/bcmdhd/dhd_common.c +++ b/drivers/net/wireless/bcmdhd/dhd_common.c @@ -1592,7 +1592,7 @@ wl_show_host_event(dhd_pub_t *dhd_pub, wl_event_msg_t *event, void *event_data, #endif /* SHOW_EVENTS */ int -wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, +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) { /* check whether packet is a BRCM event pkt */ @@ -1614,6 +1614,9 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, return (BCME_ERROR); } + if (pktlen < sizeof(bcm_event_t)) + return (BCME_ERROR); + *data_ptr = &pvt_data[1]; event_data = *data_ptr; @@ -1624,8 +1627,15 @@ wl_host_event(dhd_pub_t *dhd_pub, int *ifidx, void *pktdata, 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 af9b3d5e31e..e3be926cfa7 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -704,7 +704,7 @@ static int dhd_toe_get(dhd_info_t *dhd, int idx, uint32 *toe_ol); static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); #endif /* TOE */ -static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, +static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, size_t pktlen, wl_event_msg_t *event_ptr, void **data_ptr); #ifdef DHD_UNICAST_DHCP static const uint8 llc_snap_hdr[SNAP_HDR_LEN] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; @@ -2834,6 +2834,7 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) #else skb->mac.raw, #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */ + len - 2, &event, &data); @@ -7042,16 +7043,16 @@ dhd_get_wireless_stats(struct net_device *dev) #endif /* defined(WL_WIRELESS_EXT) */ static int -dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, +dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, size_t pktlen, wl_event_msg_t *event, void **data) { int bcmerror = 0; ASSERT(dhd != NULL); #ifdef SHOW_LOGTRACE - bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, 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, event, data, NULL); + bcmerror = wl_host_event(&dhd->pub, ifidx, pktdata, pktlen, event, data, NULL); #endif /* SHOW_LOGTRACE */ if (bcmerror != BCME_OK) -- cgit v1.2.3