mac80211: backport brcmfmac patchset with driver setting concept
[openwrt/svn-archive/archive.git] / package / kernel / mac80211 / patches / 344-0005-brcmfmac-Add-length-checks-on-firmware-events.patch
diff --git a/package/kernel/mac80211/patches/344-0005-brcmfmac-Add-length-checks-on-firmware-events.patch b/package/kernel/mac80211/patches/344-0005-brcmfmac-Add-length-checks-on-firmware-events.patch
new file mode 100644 (file)
index 0000000..3e2e350
--- /dev/null
@@ -0,0 +1,283 @@
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:54 +0100
+Subject: [PATCH] brcmfmac: Add length checks on firmware events
+
+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>
+---
+
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -3092,6 +3092,11 @@ brcmf_notify_sched_scan_results(struct b
+       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 *i
+       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) {
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c
+@@ -26,50 +26,6 @@
+ #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(stru
+               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 br
+  * 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 brc
+       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 brc
+       /* 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);
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
+@@ -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_
+                          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_ */
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/p2p.c
+@@ -1361,6 +1361,11 @@ int brcmf_p2p_notify_action_frame_rx(str
+       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_probere
+       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);