brcmfmac: Add length checks on firmware events
authorHante Meuleman <meuleman@broadcom.com>
Wed, 17 Feb 2016 10:26:54 +0000 (11:26 +0100)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 7 Mar 2016 12:14:45 +0000 (14:14 +0200)
Add additional length checks on firmware events to create more
robust code.

Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Reviewed-by: Lei Zhang <leizh@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c

index 2fd18d0..6cc8fdc 100644 (file)
@@ -3092,6 +3092,11 @@ brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
 
        brcmf_dbg(SCAN, "Enter\n");
 
+       if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+               brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+               return 0;
+       }
+
        if (e->event_code == BRCMF_E_PFN_NET_LOST) {
                brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
                return 0;
@@ -3415,6 +3420,11 @@ brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
 
        brcmf_dbg(SCAN, "Enter\n");
 
+       if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
+               brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+               return 0;
+       }
+
        pfn_result = (struct brcmf_pno_scanresults_le *)data;
 
        if (e->event_code == BRCMF_E_PFN_NET_LOST) {
index 7b26fb1..d414fbb 100644 (file)
 #include "fwil.h"
 
 /**
- * struct brcm_ethhdr - broadcom specific ether header.
- *
- * @subtype: subtype for this packet.
- * @length: TODO: length of appended data.
- * @version: version indication.
- * @oui: OUI of this packet.
- * @usr_subtype: subtype for this OUI.
- */
-struct brcm_ethhdr {
-       __be16 subtype;
-       __be16 length;
-       u8 version;
-       u8 oui[3];
-       __be16 usr_subtype;
-} __packed;
-
-struct brcmf_event_msg_be {
-       __be16 version;
-       __be16 flags;
-       __be32 event_type;
-       __be32 status;
-       __be32 reason;
-       __be32 auth_type;
-       __be32 datalen;
-       u8 addr[ETH_ALEN];
-       char ifname[IFNAMSIZ];
-       u8 ifidx;
-       u8 bsscfgidx;
-} __packed;
-
-/**
- * struct brcmf_event - contents of broadcom event packet.
- *
- * @eth: standard ether header.
- * @hdr: broadcom specific ether header.
- * @msg: common part of the actual event message.
- */
-struct brcmf_event {
-       struct ethhdr eth;
-       struct brcm_ethhdr hdr;
-       struct brcmf_event_msg_be msg;
-} __packed;
-
-/**
  * struct brcmf_fweh_queue_item - event item on event queue.
  *
  * @q: list element for queuing.
@@ -85,6 +41,7 @@ struct brcmf_fweh_queue_item {
        u8 ifidx;
        u8 ifaddr[ETH_ALEN];
        struct brcmf_event_msg_be emsg;
+       u32 datalen;
        u8 data[0];
 };
 
@@ -294,6 +251,11 @@ static void brcmf_fweh_event_worker(struct work_struct *work)
                brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data,
                                   min_t(u32, emsg.datalen, 64),
                                   "event payload, len=%d\n", emsg.datalen);
+               if (emsg.datalen > event->datalen) {
+                       brcmf_err("event invalid length header=%d, msg=%d\n",
+                                 event->datalen, emsg.datalen);
+                       goto event_free;
+               }
 
                /* special handling of interface event */
                if (event->code == BRCMF_E_IF) {
@@ -439,7 +401,8 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp)
  * dispatch the event to a registered handler (using worker).
  */
 void brcmf_fweh_process_event(struct brcmf_pub *drvr,
-                             struct brcmf_event *event_packet)
+                             struct brcmf_event *event_packet,
+                             u32 packet_len)
 {
        enum brcmf_fweh_event_code code;
        struct brcmf_fweh_info *fweh = &drvr->fweh;
@@ -459,6 +422,9 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
        if (code != BRCMF_E_IF && !fweh->evt_handler[code])
                return;
 
+       if (datalen > BRCMF_DCMD_MAXLEN)
+               return;
+
        if (in_interrupt())
                alloc_flag = GFP_ATOMIC;
 
@@ -472,6 +438,7 @@ void brcmf_fweh_process_event(struct brcmf_pub *drvr,
        /* use memcpy to get aligned event message */
        memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg));
        memcpy(event->data, data, datalen);
+       event->datalen = datalen;
        memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN);
 
        brcmf_fweh_queue_event(fweh, event);
index 5e39e2a..26ff5a9 100644 (file)
@@ -27,7 +27,6 @@
 struct brcmf_pub;
 struct brcmf_if;
 struct brcmf_cfg80211_info;
-struct brcmf_event;
 
 /* list of firmware events */
 #define BRCMF_FWEH_EVENT_ENUM_DEFLIST \
@@ -180,13 +179,55 @@ enum brcmf_fweh_event_code {
 /**
  * definitions for event packet validation.
  */
-#define BRCMF_EVENT_OUI_OFFSET         19
-#define BRCM_OUI                       "\x00\x10\x18"
-#define DOT11_OUI_LEN                  3
-#define BCMILCP_BCM_SUBTYPE_EVENT      1
+#define BRCM_OUI                               "\x00\x10\x18"
+#define BCMILCP_BCM_SUBTYPE_EVENT              1
 
 
 /**
+ * struct brcm_ethhdr - broadcom specific ether header.
+ *
+ * @subtype: subtype for this packet.
+ * @length: TODO: length of appended data.
+ * @version: version indication.
+ * @oui: OUI of this packet.
+ * @usr_subtype: subtype for this OUI.
+ */
+struct brcm_ethhdr {
+       __be16 subtype;
+       __be16 length;
+       u8 version;
+       u8 oui[3];
+       __be16 usr_subtype;
+} __packed;
+
+struct brcmf_event_msg_be {
+       __be16 version;
+       __be16 flags;
+       __be32 event_type;
+       __be32 status;
+       __be32 reason;
+       __be32 auth_type;
+       __be32 datalen;
+       u8 addr[ETH_ALEN];
+       char ifname[IFNAMSIZ];
+       u8 ifidx;
+       u8 bsscfgidx;
+} __packed;
+
+/**
+ * struct brcmf_event - contents of broadcom event packet.
+ *
+ * @eth: standard ether header.
+ * @hdr: broadcom specific ether header.
+ * @msg: common part of the actual event message.
+ */
+struct brcmf_event {
+       struct ethhdr eth;
+       struct brcm_ethhdr hdr;
+       struct brcmf_event_msg_be msg;
+} __packed;
+
+/**
  * struct brcmf_event_msg - firmware event message.
  *
  * @version: version information.
@@ -256,34 +297,35 @@ void brcmf_fweh_unregister(struct brcmf_pub *drvr,
                           enum brcmf_fweh_event_code code);
 int brcmf_fweh_activate_events(struct brcmf_if *ifp);
 void brcmf_fweh_process_event(struct brcmf_pub *drvr,
-                             struct brcmf_event *event_packet);
+                             struct brcmf_event *event_packet,
+                             u32 packet_len);
 void brcmf_fweh_p2pdev_setup(struct brcmf_if *ifp, bool ongoing);
 
 static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr,
                                          struct sk_buff *skb)
 {
        struct brcmf_event *event_packet;
-       u8 *data;
        u16 usr_stype;
 
        /* only process events when protocol matches */
        if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
                return;
 
+       if ((skb->len + ETH_HLEN) < sizeof(*event_packet))
+               return;
+
        /* check for BRCM oui match */
        event_packet = (struct brcmf_event *)skb_mac_header(skb);
-       data = (u8 *)event_packet;
-       data += BRCMF_EVENT_OUI_OFFSET;
-       if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN))
+       if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0],
+                  sizeof(event_packet->hdr.oui)))
                return;
 
        /* final match on usr_subtype */
-       data += DOT11_OUI_LEN;
-       usr_stype = get_unaligned_be16(data);
+       usr_stype = get_unaligned_be16(&event_packet->hdr.usr_subtype);
        if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT)
                return;
 
-       brcmf_fweh_process_event(drvr, event_packet);
+       brcmf_fweh_process_event(drvr, event_packet, skb->len + ETH_HLEN);
 }
 
 #endif /* FWEH_H_ */
index de5892a..b5a49e5 100644 (file)
@@ -1361,6 +1361,11 @@ int brcmf_p2p_notify_action_frame_rx(struct brcmf_if *ifp,
        u16 mgmt_type;
        u8 action;
 
+       if (e->datalen < sizeof(*rxframe)) {
+               brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+               return 0;
+       }
+
        ch.chspec = be16_to_cpu(rxframe->chanspec);
        cfg->d11inf.decchspec(&ch);
        /* Check if wpa_supplicant has registered for this frame */
@@ -1858,6 +1863,11 @@ s32 brcmf_p2p_notify_rx_mgmt_p2p_probereq(struct brcmf_if *ifp,
        brcmf_dbg(INFO, "Enter: event %d reason %d\n", e->event_code,
                  e->reason);
 
+       if (e->datalen < sizeof(*rxframe)) {
+               brcmf_dbg(SCAN, "Event data to small. Ignore\n");
+               return 0;
+       }
+
        ch.chspec = be16_to_cpu(rxframe->chanspec);
        cfg->d11inf.decchspec(&ch);