Skip to content

Commit

Permalink
CHROMIUM: net: wireless: bcmdhd: security enhancement for BRCM ether …
Browse files Browse the repository at this point in the history
…type

strict ckeckup for type and event data length

Required FW for bcm4354 >= 7.35.107.1
BUG=b:26492805

Change-Id: I1aeda8a55a57f3b9b37a802bab8bd645f53280a3
Signed-off-by: Jerry Lee <jerrylee@broadcom.com>
  • Loading branch information
Jerry Lee authored and Stephen Barber committed Jun 1, 2016
1 parent 35d57db commit dbb69bb
Showing 5 changed files with 215 additions and 55 deletions.
124 changes: 124 additions & 0 deletions drivers/net/wireless/bcmdhd/bcmevent.c
Original file line number Diff line number Diff line change
@@ -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,124 @@ 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;
}
2 changes: 2 additions & 0 deletions drivers/net/wireless/bcmdhd/dhd.h
Original file line number Diff line number Diff line change
@@ -815,6 +815,8 @@ 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,
89 changes: 50 additions & 39 deletions drivers/net/wireless/bcmdhd/dhd_common.c
Original file line number Diff line number Diff line change
@@ -107,9 +107,12 @@ 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;

@@ -1378,27 +1381,19 @@ 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 +1492,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",
__func__, 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,41 +1517,42 @@ 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;
ret = wl_host_event_get_data(pktdata, pktlen, &evu);
if (ret != BCME_OK)
return ret;

if (bcmp(BRCM_OUI, &pvt_data->bcm_hdr.oui[0], DOT11_OUI_LEN)) {
DHD_ERROR(("%s: mismatched OUI, 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;

/* 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);
}
case BCMILCP_BCM_SUBTYPE_DNGLEVENT:
/* If it is a DNGL event process it first */
dngl_host_event(dhd_pub, pktdata, &evu.dngl_event, pktlen);

if (pktlen < sizeof(bcm_event_t))
/* Return error purposely to prevent DNGL event being processed
* as BRCM event
*/
return BCME_ERROR;

*data_ptr = &pvt_data[1];
event_data = *data_ptr;

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);
39 changes: 23 additions & 16 deletions drivers/net/wireless/bcmdhd/dhd_linux.c
Original file line number Diff line number Diff line change
@@ -3012,17 +3012,24 @@ 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) &&
(len >= sizeof(bcm_event_t))) {
dhd_wl_host_event(dhd, &ifidx,
if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) {
int ret_event;

ret_event = dhd_wl_host_event(
dhd, &ifidx,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
skb_mac_header(skb),
skb_mac_header(skb),
#else
skb->mac.raw,
skb->mac.raw,
#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */
len - 2,
&event,
&data);
len,
&event,
&data);

if (ret_event != BCME_OK) {
PKTFREE(dhdp->osh, pktbuf, FALSE);
continue;
}

wl_event_to_host_order(&event);
if (!tout_ctrl)
@@ -7458,11 +7465,11 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
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)
@@ -7473,19 +7480,19 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata,
/*
* 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) */

#ifdef WL_CFG80211
ASSERT(dhd->iflist[*ifidx] != NULL);
ASSERT(dhd->iflist[*ifidx]->net != NULL);

if (dhd->iflist[*ifidx]->net)
wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data);
#endif /* defined(WL_CFG80211) */
16 changes: 16 additions & 0 deletions drivers/net/wireless/bcmdhd/include/proto/bcmevent.h
Original file line number Diff line number Diff line change
@@ -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,10 @@ 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 */

0 comments on commit dbb69bb

Please sign in to comment.