--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Date: Wed, 29 May 2019 18:02:44 +0100
+Subject: binder: Replace "%p" with "%pK" for stable
+
+From: Ben Hutchings <ben.hutchings@codethink.co.uk>
+
+This was done as part of upstream commits fdfb4a99b6ab "8inder:
+separate binder allocator structure from binder proc", 19c987241ca1
+"binder: separate out binder_alloc functions", and 7a4408c6bd3e
+"binder: make sure accesses to proc/thread are safe". However, those
+commits made lots of other changes that are not suitable for stable.
+
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/android/binder.c | 28 ++++++++++++++--------------
+ 1 file changed, 14 insertions(+), 14 deletions(-)
+
+--- a/drivers/android/binder.c
++++ b/drivers/android/binder.c
+@@ -477,7 +477,7 @@ static void binder_insert_free_buffer(st
+ new_buffer_size = binder_buffer_size(proc, new_buffer);
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: add free buffer, size %zd, at %p\n",
++ "%d: add free buffer, size %zd, at %pK\n",
+ proc->pid, new_buffer_size, new_buffer);
+
+ while (*p) {
+@@ -555,7 +555,7 @@ static int binder_update_page_range(stru
+ struct mm_struct *mm;
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: %s pages %p-%p\n", proc->pid,
++ "%d: %s pages %pK-%pK\n", proc->pid,
+ allocate ? "allocate" : "free", start, end);
+
+ if (end <= start)
+@@ -595,7 +595,7 @@ static int binder_update_page_range(stru
+ BUG_ON(*page);
+ *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
+ if (*page == NULL) {
+- pr_err("%d: binder_alloc_buf failed for page at %p\n",
++ pr_err("%d: binder_alloc_buf failed for page at %pK\n",
+ proc->pid, page_addr);
+ goto err_alloc_page_failed;
+ }
+@@ -604,7 +604,7 @@ static int binder_update_page_range(stru
+ flush_cache_vmap((unsigned long)page_addr,
+ (unsigned long)page_addr + PAGE_SIZE);
+ if (ret != 1) {
+- pr_err("%d: binder_alloc_buf failed to map page at %p in kernel\n",
++ pr_err("%d: binder_alloc_buf failed to map page at %pK in kernel\n",
+ proc->pid, page_addr);
+ goto err_map_kernel_failed;
+ }
+@@ -708,7 +708,7 @@ static struct binder_buffer *binder_allo
+ }
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: binder_alloc_buf size %zd got buffer %p size %zd\n",
++ "%d: binder_alloc_buf size %zd got buffer %pK size %zd\n",
+ proc->pid, size, buffer, buffer_size);
+
+ has_page_addr =
+@@ -738,7 +738,7 @@ static struct binder_buffer *binder_allo
+ binder_insert_free_buffer(proc, new_buffer);
+ }
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: binder_alloc_buf size %zd got %p\n",
++ "%d: binder_alloc_buf size %zd got %pK\n",
+ proc->pid, size, buffer);
+ buffer->data_size = data_size;
+ buffer->offsets_size = offsets_size;
+@@ -778,7 +778,7 @@ static void binder_delete_free_buffer(st
+ if (buffer_end_page(prev) == buffer_end_page(buffer))
+ free_page_end = 0;
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: merge free, buffer %p share page with %p\n",
++ "%d: merge free, buffer %pK share page with %pK\n",
+ proc->pid, buffer, prev);
+ }
+
+@@ -791,14 +791,14 @@ static void binder_delete_free_buffer(st
+ buffer_start_page(buffer))
+ free_page_start = 0;
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: merge free, buffer %p share page with %p\n",
++ "%d: merge free, buffer %pK share page with %pK\n",
+ proc->pid, buffer, prev);
+ }
+ }
+ list_del(&buffer->entry);
+ if (free_page_start || free_page_end) {
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: merge free, buffer %p do not share page%s%s with %p or %p\n",
++ "%d: merge free, buffer %pK do not share page%s%s with %pK or %pK\n",
+ proc->pid, buffer, free_page_start ? "" : " end",
+ free_page_end ? "" : " start", prev, next);
+ binder_update_page_range(proc, 0, free_page_start ?
+@@ -819,7 +819,7 @@ static void binder_free_buf(struct binde
+ ALIGN(buffer->offsets_size, sizeof(void *));
+
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%d: binder_free_buf %p size %zd buffer_size %zd\n",
++ "%d: binder_free_buf %pK size %zd buffer_size %zd\n",
+ proc->pid, buffer, size, buffer_size);
+
+ BUG_ON(buffer->free);
+@@ -2912,7 +2912,7 @@ static int binder_mmap(struct file *filp
+ #ifdef CONFIG_CPU_CACHE_VIPT
+ if (cache_is_vipt_aliasing()) {
+ while (CACHE_COLOUR((vma->vm_start ^ (uint32_t)proc->buffer))) {
+- pr_info("binder_mmap: %d %lx-%lx maps %p bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
++ pr_info("binder_mmap: %d %lx-%lx maps %pK bad alignment\n", proc->pid, vma->vm_start, vma->vm_end, proc->buffer);
+ vma->vm_start += PAGE_SIZE;
+ }
+ }
+@@ -3170,7 +3170,7 @@ static void binder_deferred_release(stru
+
+ page_addr = proc->buffer + i * PAGE_SIZE;
+ binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
+- "%s: %d: page %d at %p not freed\n",
++ "%s: %d: page %d at %pK not freed\n",
+ __func__, proc->pid, i, page_addr);
+ unmap_kernel_range((unsigned long)page_addr, PAGE_SIZE);
+ __free_page(proc->pages[i]);
+@@ -3271,7 +3271,7 @@ static void print_binder_transaction(str
+ static void print_binder_buffer(struct seq_file *m, const char *prefix,
+ struct binder_buffer *buffer)
+ {
+- seq_printf(m, "%s %d: %p size %zd:%zd %s\n",
++ seq_printf(m, "%s %d: %pK size %zd:%zd %s\n",
+ prefix, buffer->debug_id, buffer->data,
+ buffer->data_size, buffer->offsets_size,
+ buffer->transaction ? "active" : "delivered");
+@@ -3374,7 +3374,7 @@ static void print_binder_node(struct seq
+
+ static void print_binder_ref(struct seq_file *m, struct binder_ref *ref)
+ {
+- seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %p\n",
++ seq_printf(m, " ref %d: desc %d %snode %d s %d w %d d %pK\n",
+ ref->debug_id, ref->desc, ref->node->proc ? "" : "dead ",
+ ref->node->debug_id, ref->strong, ref->weak, ref->death);
+ }
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Todd Kjos <tkjos@android.com>
+Date: Wed, 7 Feb 2018 13:57:37 -0800
+Subject: binder: replace "%p" with "%pK"
+
+From: Todd Kjos <tkjos@android.com>
+
+commit 8ca86f1639ec5890d400fff9211aca22d0a392eb upstream.
+
+The format specifier "%p" can leak kernel addresses. Use
+"%pK" instead. There were 4 remaining cases in binder.c.
+
+Signed-off-by: Todd Kjos <tkjos@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+[bwh: Backported to 4.4: adjust context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/android/binder.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/android/binder.c
++++ b/drivers/android/binder.c
+@@ -1249,7 +1249,7 @@ static void binder_transaction_buffer_re
+ int debug_id = buffer->debug_id;
+
+ binder_debug(BINDER_DEBUG_TRANSACTION,
+- "%d buffer release %d, size %zd-%zd, failed at %p\n",
++ "%d buffer release %d, size %zd-%zd, failed at %pK\n",
+ proc->pid, buffer->debug_id,
+ buffer->data_size, buffer->offsets_size, failed_at);
+
+@@ -2105,7 +2105,7 @@ static int binder_thread_write(struct bi
+ }
+ }
+ binder_debug(BINDER_DEBUG_DEAD_BINDER,
+- "%d:%d BC_DEAD_BINDER_DONE %016llx found %p\n",
++ "%d:%d BC_DEAD_BINDER_DONE %016llx found %pK\n",
+ proc->pid, thread->pid, (u64)cookie,
+ death);
+ if (death == NULL) {
+@@ -3249,7 +3249,7 @@ static void print_binder_transaction(str
+ struct binder_transaction *t)
+ {
+ seq_printf(m,
+- "%s %d: %p from %d:%d to %d:%d code %x flags %x pri %ld r%d",
++ "%s %d: %pK from %d:%d to %d:%d code %x flags %x pri %ld r%d",
+ prefix, t->debug_id, t,
+ t->from ? t->from->proc->pid : 0,
+ t->from ? t->from->pid : 0,
+@@ -3263,7 +3263,7 @@ static void print_binder_transaction(str
+ if (t->buffer->target_node)
+ seq_printf(m, " node %d",
+ t->buffer->target_node->debug_id);
+- seq_printf(m, " size %zd:%zd data %p\n",
++ seq_printf(m, " size %zd:%zd data %pK\n",
+ t->buffer->data_size, t->buffer->offsets_size,
+ t->buffer->data);
+ }
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Daniel Axtens <dja@axtens.net>
+Date: Wed, 31 Jan 2018 14:15:34 +1100
+Subject: bnx2x: disable GSO where gso_size is too big for hardware
+
+From: Daniel Axtens <dja@axtens.net>
+
+commit 8914a595110a6eca69a5e275b323f5d09e18f4f9 upstream.
+
+If a bnx2x card is passed a GSO packet with a gso_size larger than
+~9700 bytes, it will cause a firmware error that will bring the card
+down:
+
+bnx2x: [bnx2x_attn_int_deasserted3:4323(enP24p1s0f0)]MC assert!
+bnx2x: [bnx2x_mc_assert:720(enP24p1s0f0)]XSTORM_ASSERT_LIST_INDEX 0x2
+bnx2x: [bnx2x_mc_assert:736(enP24p1s0f0)]XSTORM_ASSERT_INDEX 0x0 = 0x00000000 0x25e43e47 0x00463e01 0x00010052
+bnx2x: [bnx2x_mc_assert:750(enP24p1s0f0)]Chip Revision: everest3, FW Version: 7_13_1
+... (dump of values continues) ...
+
+Detect when the mac length of a GSO packet is greater than the maximum
+packet size (9700 bytes) and disable GSO.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
++++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+@@ -12824,6 +12824,24 @@ static netdev_features_t bnx2x_features_
+ struct net_device *dev,
+ netdev_features_t features)
+ {
++ /*
++ * A skb with gso_size + header length > 9700 will cause a
++ * firmware panic. Drop GSO support.
++ *
++ * Eventually the upper layer should not pass these packets down.
++ *
++ * For speed, if the gso_size is <= 9000, assume there will
++ * not be 700 bytes of headers and pass it through. Only do a
++ * full (slow) validation if the gso_size is > 9000.
++ *
++ * (Due to the way SKB_BY_FRAGS works this will also do a full
++ * validation in that case.)
++ */
++ if (unlikely(skb_is_gso(skb) &&
++ (skb_shinfo(skb)->gso_size > 9000) &&
++ !skb_gso_validate_mac_len(skb, 9700)))
++ features &= ~NETIF_F_GSO_MASK;
++
+ features = vlan_features_check(skb, features);
+ return vxlan_features_check(skb, features);
+ }
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Arend Van Spriel <arend.vanspriel@broadcom.com>
+Date: Thu, 6 Apr 2017 13:14:40 +0100
+Subject: brcmfmac: add length checks in scheduled scan result handler
+
+From: Arend Van Spriel <arend.vanspriel@broadcom.com>
+
+commit 4835f37e3bafc138f8bfa3cbed2920dd56fed283 upstream.
+
+Assure the event data buffer is long enough to hold the array
+of netinfo items and that SSID length does not exceed the maximum
+of 32 characters as per 802.11 spec.
+
+Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Reviewed-by: Franky Lin <franky.lin@broadcom.com>
+Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+[bwh: Backported to 4.4:
+ - Move the assignment to "data" along with the assignment to "netinfo_start"
+ that depends on it
+ - Adjust filename, context, indentation]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -3328,6 +3328,7 @@ brcmf_notify_sched_scan_results(struct b
+ struct brcmf_pno_scanresults_le *pfn_result;
+ u32 result_count;
+ u32 status;
++ u32 datalen;
+
+ brcmf_dbg(SCAN, "Enter\n");
+
+@@ -3354,6 +3355,14 @@ brcmf_notify_sched_scan_results(struct b
+ if (result_count > 0) {
+ int i;
+
++ data += sizeof(struct brcmf_pno_scanresults_le);
++ netinfo_start = (struct brcmf_pno_net_info_le *)data;
++ datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
++ if (datalen < result_count * sizeof(*netinfo)) {
++ brcmf_err("insufficient event data\n");
++ goto out_err;
++ }
++
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
+ channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
+@@ -3363,9 +3372,6 @@ brcmf_notify_sched_scan_results(struct b
+ }
+
+ request->wiphy = wiphy;
+- data += sizeof(struct brcmf_pno_scanresults_le);
+- netinfo_start = (struct brcmf_pno_net_info_le *)data;
+-
+ for (i = 0; i < result_count; i++) {
+ netinfo = &netinfo_start[i];
+ if (!netinfo) {
+@@ -3375,6 +3381,8 @@ brcmf_notify_sched_scan_results(struct b
+ goto out_err;
+ }
+
++ if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
++ netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
+ brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
+ netinfo->SSID, netinfo->channel);
+ memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Hante Meuleman <meuleman@broadcom.com>
+Date: Wed, 17 Feb 2016 11:26:54 +0100
+Subject: brcmfmac: Add length checks on firmware events
+
+From: Hante Meuleman <meuleman@broadcom.com>
+
+commit 0aedbcaf6f182690790d98d90d5fe1e64c846c34 upstream.
+
+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>
+[bwh: Backported to 4.4:
+ - Drop changes to brcmf_wowl_nd_results()
+ - Adjust filenames]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c | 5 +
+ drivers/net/wireless/brcm80211/brcmfmac/fweh.c | 57 +++--------------
+ drivers/net/wireless/brcm80211/brcmfmac/fweh.h | 68 ++++++++++++++++-----
+ drivers/net/wireless/brcm80211/brcmfmac/p2p.c | 10 +++
+ 4 files changed, 82 insertions(+), 58 deletions(-)
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/cfg80211.c
+@@ -3331,6 +3331,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;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c
++++ b/drivers/net/wireless/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/brcm80211/brcmfmac/fweh.h
++++ b/drivers/net/wireless/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/brcm80211/brcmfmac/p2p.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/p2p.c
+@@ -1365,6 +1365,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 */
+@@ -1862,6 +1867,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);
+
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Arend van Spriel <arend.vanspriel@broadcom.com>
+Date: Thu, 14 Feb 2019 13:43:48 +0100
+Subject: brcmfmac: add subtype check for event handling in data path
+
+From: Arend van Spriel <arend.vanspriel@broadcom.com>
+
+commit a4176ec356c73a46c07c181c6d04039fafa34a9f upstream.
+
+For USB there is no separate channel being used to pass events
+from firmware to the host driver and as such are passed over the
+data path. In order to detect mock event messages an additional
+check is needed on event subtype. This check is added conditionally
+using unlikely() keyword.
+
+Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Reviewed-by: Franky Lin <franky.lin@broadcom.com>
+Signed-off-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+[bwh: Backported to 4.4: adjust filenames]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/brcm80211/brcmfmac/core.c | 5 +++--
+ drivers/net/wireless/brcm80211/brcmfmac/fweh.h | 16 ++++++++++++----
+ drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 +-
+ 3 files changed, 16 insertions(+), 7 deletions(-)
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -548,7 +548,8 @@ void brcmf_rx_frame(struct device *dev,
+ } else {
+ /* Process special event packets */
+ if (handle_event)
+- brcmf_fweh_process_skb(ifp->drvr, skb);
++ brcmf_fweh_process_skb(ifp->drvr, skb,
++ BCMILCP_SUBTYPE_VENDOR_LONG);
+
+ brcmf_netif_rx(ifp, skb);
+ }
+@@ -575,7 +576,7 @@ void brcmf_rx_event(struct device *dev,
+
+ skb->protocol = eth_type_trans(skb, ifp->ndev);
+
+- brcmf_fweh_process_skb(ifp->drvr, skb);
++ brcmf_fweh_process_skb(ifp->drvr, skb, 0);
+ brcmu_pkt_buf_free_skb(skb);
+ }
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h
+@@ -181,7 +181,7 @@ enum brcmf_fweh_event_code {
+ */
+ #define BRCM_OUI "\x00\x10\x18"
+ #define BCMILCP_BCM_SUBTYPE_EVENT 1
+-
++#define BCMILCP_SUBTYPE_VENDOR_LONG 32769
+
+ /**
+ * struct brcm_ethhdr - broadcom specific ether header.
+@@ -302,10 +302,10 @@ void brcmf_fweh_process_event(struct brc
+ 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 sk_buff *skb, u16 stype)
+ {
+ struct brcmf_event *event_packet;
+- u16 usr_stype;
++ u16 subtype, usr_stype;
+
+ /* only process events when protocol matches */
+ if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL))
+@@ -314,8 +314,16 @@ static inline void brcmf_fweh_process_sk
+ if ((skb->len + ETH_HLEN) < sizeof(*event_packet))
+ return;
+
+- /* check for BRCM oui match */
+ event_packet = (struct brcmf_event *)skb_mac_header(skb);
++
++ /* check subtype if needed */
++ if (unlikely(stype)) {
++ subtype = get_unaligned_be16(&event_packet->hdr.subtype);
++ if (subtype != stype)
++ return;
++ }
++
++ /* check for BRCM oui match */
+ if (memcmp(BRCM_OUI, &event_packet->hdr.oui[0],
+ sizeof(event_packet->hdr.oui)))
+ return;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -1112,7 +1112,7 @@ static void brcmf_msgbuf_process_event(s
+
+ skb->protocol = eth_type_trans(skb, ifp->ndev);
+
+- brcmf_fweh_process_skb(ifp->drvr, skb);
++ brcmf_fweh_process_skb(ifp->drvr, skb, 0);
+
+ exit:
+ brcmu_pkt_buf_free_skb(skb);
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Gavin Li <git@thegavinli.com>
+Date: Tue, 17 Jan 2017 15:24:05 -0800
+Subject: brcmfmac: fix incorrect event channel deduction
+
+From: Gavin Li <git@thegavinli.com>
+
+commit 8e290cecdd0178f3d4cf7d463c51dc7e462843b4 upstream.
+
+brcmf_sdio_fromevntchan() was being called on the the data frame
+rather than the software header, causing some frames to be
+mischaracterized as on the event channel rather than the data channel.
+
+This fixes a major performance regression (due to dropped packets). With
+this patch the download speed jumped from 1Mbit/s back up to 40MBit/s due
+to the sheer amount of packets being incorrectly processed.
+
+Fixes: c56caa9db8ab ("brcmfmac: screening firmware event packet")
+Signed-off-by: Gavin Li <git@thegavinli.com>
+Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
+[kvalo@codeaurora.org: improve commit logs based on email discussion]
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+[bwh: Backported to 4.4: adjust filename]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -1765,7 +1765,7 @@ static u8 brcmf_sdio_rxglom(struct brcmf
+ pfirst->len, pfirst->next,
+ pfirst->prev);
+ skb_unlink(pfirst, &bus->glom);
+- if (brcmf_sdio_fromevntchan(pfirst->data))
++ if (brcmf_sdio_fromevntchan(&dptr[SDPCM_HWHDR_LEN]))
+ brcmf_rx_event(bus->sdiodev->dev, pfirst);
+ else
+ brcmf_rx_frame(bus->sdiodev->dev, pfirst,
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Arend van Spriel <arend@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:27 +0200
+Subject: brcmfmac: revise handling events in receive path
+
+From: Arend van Spriel <arend@broadcom.com>
+
+commit 9c349892ccc90c6de2baaa69cc78449f58082273 upstream.
+
+Move event handling out of brcmf_netif_rx() avoiding the need
+to pass a flag. This flag is only ever true for USB hosts as
+other interface use separate brcmf_rx_event() function.
+
+Reviewed-by: Hante Meuleman <hante.meuleman@broadcom.com>
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Reviewed-by: Franky Lin <franky.lin@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+[bwh: Backported to 4.4 as dependency of commit a4176ec356c7
+ "brcmfmac: add subtype check for event handling in data path"
+ - Adjust filenames, context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/brcm80211/brcmfmac/bus.h | 2 -
+ drivers/net/wireless/brcm80211/brcmfmac/core.c | 32 +++++++++++------------
+ drivers/net/wireless/brcm80211/brcmfmac/core.h | 3 --
+ drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 2 -
+ 4 files changed, 19 insertions(+), 20 deletions(-)
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
+@@ -214,7 +214,7 @@ bool brcmf_c_prec_enq(struct device *dev
+ int prec);
+
+ /* Receive frame for delivery to OS. Callee disposes of rxp. */
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_evnt);
++void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_event);
+ /* Receive async event packet from firmware. Callee disposes of rxp. */
+ void brcmf_rx_event(struct device *dev, struct sk_buff *rxp);
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -301,18 +301,11 @@ void brcmf_txflowblock(struct device *de
+ brcmf_fws_bus_blocked(drvr, state);
+ }
+
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
+- bool handle_event)
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
+ {
+- skb->protocol = eth_type_trans(skb, ifp->ndev);
+-
+ if (skb->pkt_type == PACKET_MULTICAST)
+ ifp->stats.multicast++;
+
+- /* Process special event packets */
+- if (handle_event)
+- brcmf_fweh_process_skb(ifp->drvr, skb);
+-
+ if (!(ifp->ndev->flags & IFF_UP)) {
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+@@ -372,7 +365,7 @@ static void brcmf_rxreorder_process_info
+ /* validate flags and flow id */
+ if (flags == 0xFF) {
+ brcmf_err("invalid flags...so ignore this packet\n");
+- brcmf_netif_rx(ifp, pkt, false);
++ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+@@ -384,7 +377,7 @@ static void brcmf_rxreorder_process_info
+ if (rfi == NULL) {
+ brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ flow_id);
+- brcmf_netif_rx(ifp, pkt, false);
++ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+@@ -409,7 +402,7 @@ static void brcmf_rxreorder_process_info
+ rfi = kzalloc(buf_size, GFP_ATOMIC);
+ if (rfi == NULL) {
+ brcmf_err("failed to alloc buffer\n");
+- brcmf_netif_rx(ifp, pkt, false);
++ brcmf_netif_rx(ifp, pkt);
+ return;
+ }
+
+@@ -523,11 +516,11 @@ static void brcmf_rxreorder_process_info
+ netif_rx:
+ skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ __skb_unlink(pkt, &reorder_list);
+- brcmf_netif_rx(ifp, pkt, false);
++ brcmf_netif_rx(ifp, pkt);
+ }
+ }
+
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_evnt)
++void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_event)
+ {
+ struct brcmf_if *ifp;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+@@ -547,11 +540,18 @@ void brcmf_rx_frame(struct device *dev,
+ return;
+ }
+
++ skb->protocol = eth_type_trans(skb, ifp->ndev);
++
+ rd = (struct brcmf_skb_reorder_data *)skb->cb;
+- if (rd->reorder)
++ if (rd->reorder) {
+ brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
+- else
+- brcmf_netif_rx(ifp, skb, handle_evnt);
++ } else {
++ /* Process special event packets */
++ if (handle_event)
++ brcmf_fweh_process_skb(ifp->drvr, skb);
++
++ brcmf_netif_rx(ifp, skb);
++ }
+ }
+
+ void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -215,8 +215,7 @@ int brcmf_get_next_free_bsscfgidx(struct
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state);
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
+- bool handle_event);
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
+ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+
+ #endif /* BRCMFMAC_CORE_H */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -1155,7 +1155,7 @@ brcmf_msgbuf_process_rx_complete(struct
+ brcmu_pkt_buf_free_skb(skb);
+ return;
+ }
+- brcmf_netif_rx(ifp, skb, false);
++ brcmf_netif_rx(ifp, skb);
+ }
+
+
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Franky Lin <franky.lin@broadcom.com>
+Date: Mon, 11 Apr 2016 11:35:25 +0200
+Subject: brcmfmac: screening firmware event packet
+
+From: Franky Lin <franky.lin@broadcom.com>
+
+commit c56caa9db8abbbfb9e31325e0897705aa897db37 upstream.
+
+Firmware uses asynchronized events as a communication method to the
+host. The event packets are marked as ETH_P_LINK_CTL protocol type. For
+SDIO and PCIe bus, this kind of packets are delivered through virtual
+event channel not data channel. This patch adds a screening logic to
+make sure the event handler only processes the events coming from the
+correct channel.
+
+Reviewed-by: Pieter-Paul Giesberts <pieter-paul.giesberts@broadcom.com>
+Signed-off-by: Franky Lin <franky.lin@broadcom.com>
+Signed-off-by: Arend van Spriel <arend@broadcom.com>
+Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
+[bwh: Backported to 4.4 adjust filenames]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/brcm80211/brcmfmac/bus.h | 4 +-
+ drivers/net/wireless/brcm80211/brcmfmac/core.c | 46 ++++++++++++++++++-----
+ drivers/net/wireless/brcm80211/brcmfmac/core.h | 3 +
+ drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c | 42 ++++++++++++---------
+ drivers/net/wireless/brcm80211/brcmfmac/sdio.c | 32 ++++++++++++----
+ drivers/net/wireless/brcm80211/brcmfmac/usb.c | 2 -
+ 6 files changed, 90 insertions(+), 39 deletions(-)
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/bus.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/bus.h
+@@ -214,7 +214,9 @@ bool brcmf_c_prec_enq(struct device *dev
+ int prec);
+
+ /* Receive frame for delivery to OS. Callee disposes of rxp. */
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp);
++void brcmf_rx_frame(struct device *dev, struct sk_buff *rxp, bool handle_evnt);
++/* Receive async event packet from firmware. Callee disposes of rxp. */
++void brcmf_rx_event(struct device *dev, struct sk_buff *rxp);
+
+ /* Indication from bus module regarding presence/insertion of dongle. */
+ int brcmf_attach(struct device *dev);
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.c
+@@ -301,16 +301,17 @@ void brcmf_txflowblock(struct device *de
+ brcmf_fws_bus_blocked(drvr, state);
+ }
+
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb)
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
++ bool handle_event)
+ {
+- skb->dev = ifp->ndev;
+- skb->protocol = eth_type_trans(skb, skb->dev);
++ skb->protocol = eth_type_trans(skb, ifp->ndev);
+
+ if (skb->pkt_type == PACKET_MULTICAST)
+ ifp->stats.multicast++;
+
+ /* Process special event packets */
+- brcmf_fweh_process_skb(ifp->drvr, skb);
++ if (handle_event)
++ brcmf_fweh_process_skb(ifp->drvr, skb);
+
+ if (!(ifp->ndev->flags & IFF_UP)) {
+ brcmu_pkt_buf_free_skb(skb);
+@@ -371,7 +372,7 @@ static void brcmf_rxreorder_process_info
+ /* validate flags and flow id */
+ if (flags == 0xFF) {
+ brcmf_err("invalid flags...so ignore this packet\n");
+- brcmf_netif_rx(ifp, pkt);
++ brcmf_netif_rx(ifp, pkt, false);
+ return;
+ }
+
+@@ -383,7 +384,7 @@ static void brcmf_rxreorder_process_info
+ if (rfi == NULL) {
+ brcmf_dbg(INFO, "received flags to cleanup, but no flow (%d) yet\n",
+ flow_id);
+- brcmf_netif_rx(ifp, pkt);
++ brcmf_netif_rx(ifp, pkt, false);
+ return;
+ }
+
+@@ -408,7 +409,7 @@ static void brcmf_rxreorder_process_info
+ rfi = kzalloc(buf_size, GFP_ATOMIC);
+ if (rfi == NULL) {
+ brcmf_err("failed to alloc buffer\n");
+- brcmf_netif_rx(ifp, pkt);
++ brcmf_netif_rx(ifp, pkt, false);
+ return;
+ }
+
+@@ -522,11 +523,11 @@ static void brcmf_rxreorder_process_info
+ netif_rx:
+ skb_queue_walk_safe(&reorder_list, pkt, pnext) {
+ __skb_unlink(pkt, &reorder_list);
+- brcmf_netif_rx(ifp, pkt);
++ brcmf_netif_rx(ifp, pkt, false);
+ }
+ }
+
+-void brcmf_rx_frame(struct device *dev, struct sk_buff *skb)
++void brcmf_rx_frame(struct device *dev, struct sk_buff *skb, bool handle_evnt)
+ {
+ struct brcmf_if *ifp;
+ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
+@@ -550,7 +551,32 @@ void brcmf_rx_frame(struct device *dev,
+ if (rd->reorder)
+ brcmf_rxreorder_process_info(ifp, rd->reorder, skb);
+ else
+- brcmf_netif_rx(ifp, skb);
++ brcmf_netif_rx(ifp, skb, handle_evnt);
++}
++
++void brcmf_rx_event(struct device *dev, struct sk_buff *skb)
++{
++ struct brcmf_if *ifp;
++ struct brcmf_bus *bus_if = dev_get_drvdata(dev);
++ struct brcmf_pub *drvr = bus_if->drvr;
++ int ret;
++
++ brcmf_dbg(EVENT, "Enter: %s: rxp=%p\n", dev_name(dev), skb);
++
++ /* process and remove protocol-specific header */
++ ret = brcmf_proto_hdrpull(drvr, true, skb, &ifp);
++
++ if (ret || !ifp || !ifp->ndev) {
++ if (ret != -ENODATA && ifp)
++ ifp->stats.rx_errors++;
++ brcmu_pkt_buf_free_skb(skb);
++ return;
++ }
++
++ skb->protocol = eth_type_trans(skb, ifp->ndev);
++
++ brcmf_fweh_process_skb(ifp->drvr, skb);
++ brcmu_pkt_buf_free_skb(skb);
+ }
+
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success)
+--- a/drivers/net/wireless/brcm80211/brcmfmac/core.h
++++ b/drivers/net/wireless/brcm80211/brcmfmac/core.h
+@@ -215,7 +215,8 @@ int brcmf_get_next_free_bsscfgidx(struct
+ void brcmf_txflowblock_if(struct brcmf_if *ifp,
+ enum brcmf_netif_stop_reason reason, bool state);
+ void brcmf_txfinalize(struct brcmf_if *ifp, struct sk_buff *txp, bool success);
+-void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb);
++void brcmf_netif_rx(struct brcmf_if *ifp, struct sk_buff *skb,
++ bool handle_event);
+ void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
+
+ #endif /* BRCMFMAC_CORE_H */
+--- a/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/msgbuf.c
+@@ -20,6 +20,7 @@
+
+ #include <linux/types.h>
+ #include <linux/netdevice.h>
++#include <linux/etherdevice.h>
+
+ #include <brcmu_utils.h>
+ #include <brcmu_wifi.h>
+@@ -1076,28 +1077,13 @@ static void brcmf_msgbuf_rxbuf_event_pos
+ }
+
+
+-static void
+-brcmf_msgbuf_rx_skb(struct brcmf_msgbuf *msgbuf, struct sk_buff *skb,
+- u8 ifidx)
+-{
+- struct brcmf_if *ifp;
+-
+- ifp = brcmf_get_ifp(msgbuf->drvr, ifidx);
+- if (!ifp || !ifp->ndev) {
+- brcmf_err("Received pkt for invalid ifidx %d\n", ifidx);
+- brcmu_pkt_buf_free_skb(skb);
+- return;
+- }
+- brcmf_netif_rx(ifp, skb);
+-}
+-
+-
+ static void brcmf_msgbuf_process_event(struct brcmf_msgbuf *msgbuf, void *buf)
+ {
+ struct msgbuf_rx_event *event;
+ u32 idx;
+ u16 buflen;
+ struct sk_buff *skb;
++ struct brcmf_if *ifp;
+
+ event = (struct msgbuf_rx_event *)buf;
+ idx = le32_to_cpu(event->msg.request_id);
+@@ -1117,7 +1103,19 @@ static void brcmf_msgbuf_process_event(s
+
+ skb_trim(skb, buflen);
+
+- brcmf_msgbuf_rx_skb(msgbuf, skb, event->msg.ifidx);
++ ifp = brcmf_get_ifp(msgbuf->drvr, event->msg.ifidx);
++ if (!ifp || !ifp->ndev) {
++ brcmf_err("Received pkt for invalid ifidx %d\n",
++ event->msg.ifidx);
++ goto exit;
++ }
++
++ skb->protocol = eth_type_trans(skb, ifp->ndev);
++
++ brcmf_fweh_process_skb(ifp->drvr, skb);
++
++exit:
++ brcmu_pkt_buf_free_skb(skb);
+ }
+
+
+@@ -1129,6 +1127,7 @@ brcmf_msgbuf_process_rx_complete(struct
+ u16 data_offset;
+ u16 buflen;
+ u32 idx;
++ struct brcmf_if *ifp;
+
+ brcmf_msgbuf_update_rxbufpost_count(msgbuf, 1);
+
+@@ -1149,7 +1148,14 @@ brcmf_msgbuf_process_rx_complete(struct
+
+ skb_trim(skb, buflen);
+
+- brcmf_msgbuf_rx_skb(msgbuf, skb, rx_complete->msg.ifidx);
++ ifp = brcmf_get_ifp(msgbuf->drvr, rx_complete->msg.ifidx);
++ if (!ifp || !ifp->ndev) {
++ brcmf_err("Received pkt for invalid ifidx %d\n",
++ rx_complete->msg.ifidx);
++ brcmu_pkt_buf_free_skb(skb);
++ return;
++ }
++ brcmf_netif_rx(ifp, skb, false);
+ }
+
+
+--- a/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio.c
+@@ -1394,6 +1394,17 @@ static inline u8 brcmf_sdio_getdatoffset
+ return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT);
+ }
+
++static inline bool brcmf_sdio_fromevntchan(u8 *swheader)
++{
++ u32 hdrvalue;
++ u8 ret;
++
++ hdrvalue = *(u32 *)swheader;
++ ret = (u8)((hdrvalue & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT);
++
++ return (ret == SDPCM_EVENT_CHANNEL);
++}
++
+ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,
+ struct brcmf_sdio_hdrinfo *rd,
+ enum brcmf_sdio_frmtype type)
+@@ -1754,7 +1765,11 @@ static u8 brcmf_sdio_rxglom(struct brcmf
+ pfirst->len, pfirst->next,
+ pfirst->prev);
+ skb_unlink(pfirst, &bus->glom);
+- brcmf_rx_frame(bus->sdiodev->dev, pfirst);
++ if (brcmf_sdio_fromevntchan(pfirst->data))
++ brcmf_rx_event(bus->sdiodev->dev, pfirst);
++ else
++ brcmf_rx_frame(bus->sdiodev->dev, pfirst,
++ false);
+ bus->sdcnt.rxglompkts++;
+ }
+
+@@ -2081,18 +2096,19 @@ static uint brcmf_sdio_readframes(struct
+ __skb_trim(pkt, rd->len);
+ skb_pull(pkt, rd->dat_offset);
+
++ if (pkt->len == 0)
++ brcmu_pkt_buf_free_skb(pkt);
++ else if (rd->channel == SDPCM_EVENT_CHANNEL)
++ brcmf_rx_event(bus->sdiodev->dev, pkt);
++ else
++ brcmf_rx_frame(bus->sdiodev->dev, pkt,
++ false);
++
+ /* prepare the descriptor for the next read */
+ rd->len = rd->len_nxtfrm << 4;
+ rd->len_nxtfrm = 0;
+ /* treat all packet as event if we don't know */
+ rd->channel = SDPCM_EVENT_CHANNEL;
+-
+- if (pkt->len == 0) {
+- brcmu_pkt_buf_free_skb(pkt);
+- continue;
+- }
+-
+- brcmf_rx_frame(bus->sdiodev->dev, pkt);
+ }
+
+ rxcount = maxframes - rxleft;
+--- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c
++++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c
+@@ -502,7 +502,7 @@ static void brcmf_usb_rx_complete(struct
+
+ if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) {
+ skb_put(skb, urb->actual_length);
+- brcmf_rx_frame(devinfo->dev, skb);
++ brcmf_rx_frame(devinfo->dev, skb, true);
+ brcmf_usb_rx_refill(devinfo, req);
+ } else {
+ brcmu_pkt_buf_free_skb(skb);
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Andrea Arcangeli <aarcange@redhat.com>
+Date: Thu, 18 Apr 2019 17:50:52 -0700
+Subject: coredump: fix race condition between mmget_not_zero()/get_task_mm() and core dumping
+
+From: Andrea Arcangeli <aarcange@redhat.com>
+
+commit 04f5866e41fb70690e28397487d8bd8eea7d712a upstream.
+
+The core dumping code has always run without holding the mmap_sem for
+writing, despite that is the only way to ensure that the entire vma
+layout will not change from under it. Only using some signal
+serialization on the processes belonging to the mm is not nearly enough.
+This was pointed out earlier. For example in Hugh's post from Jul 2017:
+
+ https://lkml.kernel.org/r/alpine.LSU.2.11.1707191716030.2055@eggly.anvils
+
+ "Not strictly relevant here, but a related note: I was very surprised
+ to discover, only quite recently, how handle_mm_fault() may be called
+ without down_read(mmap_sem) - when core dumping. That seems a
+ misguided optimization to me, which would also be nice to correct"
+
+In particular because the growsdown and growsup can move the
+vm_start/vm_end the various loops the core dump does around the vma will
+not be consistent if page faults can happen concurrently.
+
+Pretty much all users calling mmget_not_zero()/get_task_mm() and then
+taking the mmap_sem had the potential to introduce unexpected side
+effects in the core dumping code.
+
+Adding mmap_sem for writing around the ->core_dump invocation is a
+viable long term fix, but it requires removing all copy user and page
+faults and to replace them with get_dump_page() for all binary formats
+which is not suitable as a short term fix.
+
+For the time being this solution manually covers the places that can
+confuse the core dump either by altering the vma layout or the vma flags
+while it runs. Once ->core_dump runs under mmap_sem for writing the
+function mmget_still_valid() can be dropped.
+
+Allowing mmap_sem protected sections to run in parallel with the
+coredump provides some minor parallelism advantage to the swapoff code
+(which seems to be safe enough by never mangling any vma field and can
+keep doing swapins in parallel to the core dumping) and to some other
+corner case.
+
+In order to facilitate the backporting I added "Fixes: 86039bd3b4e6"
+however the side effect of this same race condition in /proc/pid/mem
+should be reproducible since before 2.6.12-rc2 so I couldn't add any
+other "Fixes:" because there's no hash beyond the git genesis commit.
+
+Because find_extend_vma() is the only location outside of the process
+context that could modify the "mm" structures under mmap_sem for
+reading, by adding the mmget_still_valid() check to it, all other cases
+that take the mmap_sem for reading don't need the new check after
+mmget_not_zero()/get_task_mm(). The expand_stack() in page fault
+context also doesn't need the new check, because all tasks under core
+dumping are frozen.
+
+Link: http://lkml.kernel.org/r/20190325224949.11068-1-aarcange@redhat.com
+Fixes: 86039bd3b4e6 ("userfaultfd: add new syscall to provide memory externalization")
+Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
+Reported-by: Jann Horn <jannh@google.com>
+Suggested-by: Oleg Nesterov <oleg@redhat.com>
+Acked-by: Peter Xu <peterx@redhat.com>
+Reviewed-by: Mike Rapoport <rppt@linux.ibm.com>
+Reviewed-by: Oleg Nesterov <oleg@redhat.com>
+Reviewed-by: Jann Horn <jannh@google.com>
+Acked-by: Jason Gunthorpe <jgg@mellanox.com>
+Acked-by: Michal Hocko <mhocko@suse.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+[bwh: Backported to 4.4:
+ - Drop changes in Infiniband and userfaultfd_event_wait_completion()
+ - Adjust filename, context]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/proc/task_mmu.c | 18 ++++++++++++++++++
+ fs/userfaultfd.c | 7 +++++++
+ include/linux/sched.h | 21 +++++++++++++++++++++
+ mm/mmap.c | 7 ++++++-
+ 4 files changed, 52 insertions(+), 1 deletion(-)
+
+--- a/fs/proc/task_mmu.c
++++ b/fs/proc/task_mmu.c
+@@ -947,6 +947,24 @@ static ssize_t clear_refs_write(struct f
+ continue;
+ up_read(&mm->mmap_sem);
+ down_write(&mm->mmap_sem);
++ /*
++ * Avoid to modify vma->vm_flags
++ * without locked ops while the
++ * coredump reads the vm_flags.
++ */
++ if (!mmget_still_valid(mm)) {
++ /*
++ * Silently return "count"
++ * like if get_task_mm()
++ * failed. FIXME: should this
++ * function have returned
++ * -ESRCH if get_task_mm()
++ * failed like if
++ * get_proc_task() fails?
++ */
++ up_write(&mm->mmap_sem);
++ goto out_mm;
++ }
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ vma->vm_flags &= ~VM_SOFTDIRTY;
+ vma_set_page_prot(vma);
+--- a/fs/userfaultfd.c
++++ b/fs/userfaultfd.c
+@@ -446,6 +446,8 @@ static int userfaultfd_release(struct in
+ * taking the mmap_sem for writing.
+ */
+ down_write(&mm->mmap_sem);
++ if (!mmget_still_valid(mm))
++ goto skip_mm;
+ prev = NULL;
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ cond_resched();
+@@ -468,6 +470,7 @@ static int userfaultfd_release(struct in
+ vma->vm_flags = new_flags;
+ vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
+ }
++skip_mm:
+ up_write(&mm->mmap_sem);
+ mmput(mm);
+ wakeup:
+@@ -769,6 +772,8 @@ static int userfaultfd_register(struct u
+ goto out;
+
+ down_write(&mm->mmap_sem);
++ if (!mmget_still_valid(mm))
++ goto out_unlock;
+ vma = find_vma_prev(mm, start, &prev);
+ if (!vma)
+ goto out_unlock;
+@@ -914,6 +919,8 @@ static int userfaultfd_unregister(struct
+ goto out;
+
+ down_write(&mm->mmap_sem);
++ if (!mmget_still_valid(mm))
++ goto out_unlock;
+ vma = find_vma_prev(mm, start, &prev);
+ if (!vma)
+ goto out_unlock;
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -2625,6 +2625,27 @@ static inline bool mmget_not_zero(struct
+ return atomic_inc_not_zero(&mm->mm_users);
+ }
+
++/*
++ * This has to be called after a get_task_mm()/mmget_not_zero()
++ * followed by taking the mmap_sem for writing before modifying the
++ * vmas or anything the coredump pretends not to change from under it.
++ *
++ * NOTE: find_extend_vma() called from GUP context is the only place
++ * that can modify the "mm" (notably the vm_start/end) under mmap_sem
++ * for reading and outside the context of the process, so it is also
++ * the only case that holds the mmap_sem for reading that must call
++ * this function. Generally if the mmap_sem is hold for reading
++ * there's no need of this check after get_task_mm()/mmget_not_zero().
++ *
++ * This function can be obsoleted and the check can be removed, after
++ * the coredump code will hold the mmap_sem for writing before
++ * invoking the ->core_dump methods.
++ */
++static inline bool mmget_still_valid(struct mm_struct *mm)
++{
++ return likely(!mm->core_state);
++}
++
+ /* mmput gets rid of the mappings and all user-space */
+ extern void mmput(struct mm_struct *);
+ /* Grab a reference to a task's mm, if it is not already going away */
+--- a/mm/mmap.c
++++ b/mm/mmap.c
+@@ -42,6 +42,7 @@
+ #include <linux/memory.h>
+ #include <linux/printk.h>
+ #include <linux/userfaultfd_k.h>
++#include <linux/sched.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/cacheflush.h>
+@@ -2398,7 +2399,8 @@ find_extend_vma(struct mm_struct *mm, un
+ vma = find_vma_prev(mm, addr, &prev);
+ if (vma && (vma->vm_start <= addr))
+ return vma;
+- if (!prev || expand_stack(prev, addr))
++ /* don't alter vm_end if the coredump is running */
++ if (!prev || !mmget_still_valid(mm) || expand_stack(prev, addr))
+ return NULL;
+ if (prev->vm_flags & VM_LOCKED)
+ populate_vma_page_range(prev, addr, prev->vm_end, NULL);
+@@ -2424,6 +2426,9 @@ find_extend_vma(struct mm_struct *mm, un
+ return vma;
+ if (!(vma->vm_flags & VM_GROWSDOWN))
+ return NULL;
++ /* don't alter vm_start if the coredump is running */
++ if (!mmget_still_valid(mm))
++ return NULL;
+ start = vma->vm_start;
+ if (expand_stack(vma, addr))
+ return NULL;
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Daniel Axtens <dja@axtens.net>
+Date: Wed, 31 Jan 2018 14:15:33 +1100
+Subject: net: create skb_gso_validate_mac_len()
+
+From: Daniel Axtens <dja@axtens.net>
+
+commit 2b16f048729bf35e6c28a40cbfad07239f9dcd90 upstream.
+
+If you take a GSO skb, and split it into packets, will the MAC
+length (L2 + L3 + L4 headers + payload) of those packets be small
+enough to fit within a given length?
+
+Move skb_gso_mac_seglen() to skbuff.h with other related functions
+like skb_gso_network_seglen() so we can use it, and then create
+skb_gso_validate_mac_len to do the full calculation.
+
+Signed-off-by: Daniel Axtens <dja@axtens.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+[bwh: Backported to 4.4: There is no GSO_BY_FRAGS case to handle, so
+ skb_gso_validate_mac_len() becomes a trivial comparison. Put it inline in
+ <linux/skbuff.h>.]
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/skbuff.h | 30 ++++++++++++++++++++++++++++++
+ net/sched/sch_tbf.c | 10 ----------
+ 2 files changed, 30 insertions(+), 10 deletions(-)
+
+--- a/include/linux/skbuff.h
++++ b/include/linux/skbuff.h
+@@ -3664,5 +3664,35 @@ static inline unsigned int skb_gso_netwo
+ return hdr_len + skb_gso_transport_seglen(skb);
+ }
+
++/**
++ * skb_gso_mac_seglen - Return length of individual segments of a gso packet
++ *
++ * @skb: GSO skb
++ *
++ * skb_gso_mac_seglen is used to determine the real size of the
++ * individual segments, including MAC/L2, Layer3 (IP, IPv6) and L4
++ * headers (TCP/UDP).
++ */
++static inline unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
++{
++ unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
++ return hdr_len + skb_gso_transport_seglen(skb);
++}
++
++/**
++ * skb_gso_validate_mac_len - Will a split GSO skb fit in a given length?
++ *
++ * @skb: GSO skb
++ * @len: length to validate against
++ *
++ * skb_gso_validate_mac_len validates if a given skb will fit a wanted
++ * length once split, including L2, L3 and L4 headers and the payload.
++ */
++static inline bool
++skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len)
++{
++ return skb_gso_mac_seglen(skb) <= len;
++}
++
+ #endif /* __KERNEL__ */
+ #endif /* _LINUX_SKBUFF_H */
+--- a/net/sched/sch_tbf.c
++++ b/net/sched/sch_tbf.c
+@@ -142,16 +142,6 @@ static u64 psched_ns_t2l(const struct ps
+ return len;
+ }
+
+-/*
+- * Return length of individual segments of a gso packet,
+- * including all headers (MAC, IP, TCP/UDP)
+- */
+-static unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
+-{
+- unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
+- return hdr_len + skb_gso_transport_seglen(skb);
+-}
+-
+ /* GSO packet is too big, segment it so that tbf can transmit
+ * each segment in time
+ */
kernel-signal.c-trace_signal_deliver-when-signal_group_exit.patch
cifs-cifs_read_allocate_pages-don-t-iterate-through-whole-page-array-on-enomem.patch
drm-rockchip-shutdown-drm-subsystem-on-shutdown.patch
+binder-replace-p-with-pk-for-stable.patch
+binder-replace-p-with-pk.patch
+net-create-skb_gso_validate_mac_len.patch
+bnx2x-disable-gso-where-gso_size-is-too-big-for-hardware.patch
+brcmfmac-add-length-checks-on-firmware-events.patch
+brcmfmac-screening-firmware-event-packet.patch
+brcmfmac-revise-handling-events-in-receive-path.patch
+brcmfmac-fix-incorrect-event-channel-deduction.patch
+brcmfmac-add-length-checks-in-scheduled-scan-result-handler.patch
+brcmfmac-add-subtype-check-for-event-handling-in-data-path.patch
+userfaultfd-don-t-pin-the-user-memory-in-userfaultfd_file_create.patch
+coredump-fix-race-condition-between-mmget_not_zero-get_task_mm-and-core-dumping.patch
--- /dev/null
+From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Fri, 20 May 2016 16:58:36 -0700
+Subject: userfaultfd: don't pin the user memory in userfaultfd_file_create()
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+commit d2005e3f41d4f9299e2df6a967c8beb5086967a9 upstream.
+
+userfaultfd_file_create() increments mm->mm_users; this means that the
+memory won't be unmapped/freed if mm owner exits/execs, and UFFDIO_COPY
+after that can populate the orphaned mm more.
+
+Change userfaultfd_file_create() and userfaultfd_ctx_put() to use
+mm->mm_count to pin mm_struct. This means that
+atomic_inc_not_zero(mm->mm_users) is needed when we are going to
+actually play with this memory. Except handle_userfault() path doesn't
+need this, the caller must already have a reference.
+
+The patch adds the new trivial helper, mmget_not_zero(), it can have
+more users.
+
+Link: http://lkml.kernel.org/r/20160516172254.GA8595@redhat.com
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Cc: Andrea Arcangeli <aarcange@redhat.com>
+Cc: Michal Hocko <mhocko@kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/userfaultfd.c | 41 ++++++++++++++++++++++++++++-------------
+ include/linux/sched.h | 7 ++++++-
+ 2 files changed, 34 insertions(+), 14 deletions(-)
+
+--- a/fs/userfaultfd.c
++++ b/fs/userfaultfd.c
+@@ -137,7 +137,7 @@ static void userfaultfd_ctx_put(struct u
+ VM_BUG_ON(waitqueue_active(&ctx->fault_wqh));
+ VM_BUG_ON(spin_is_locked(&ctx->fd_wqh.lock));
+ VM_BUG_ON(waitqueue_active(&ctx->fd_wqh));
+- mmput(ctx->mm);
++ mmdrop(ctx->mm);
+ kmem_cache_free(userfaultfd_ctx_cachep, ctx);
+ }
+ }
+@@ -434,6 +434,9 @@ static int userfaultfd_release(struct in
+
+ ACCESS_ONCE(ctx->released) = true;
+
++ if (!mmget_not_zero(mm))
++ goto wakeup;
++
+ /*
+ * Flush page faults out of all CPUs. NOTE: all page faults
+ * must be retried without returning VM_FAULT_SIGBUS if
+@@ -466,7 +469,8 @@ static int userfaultfd_release(struct in
+ vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
+ }
+ up_write(&mm->mmap_sem);
+-
++ mmput(mm);
++wakeup:
+ /*
+ * After no new page faults can wait on this fault_*wqh, flush
+ * the last page faults that may have been already waiting on
+@@ -760,10 +764,12 @@ static int userfaultfd_register(struct u
+ start = uffdio_register.range.start;
+ end = start + uffdio_register.range.len;
+
++ ret = -ENOMEM;
++ if (!mmget_not_zero(mm))
++ goto out;
++
+ down_write(&mm->mmap_sem);
+ vma = find_vma_prev(mm, start, &prev);
+-
+- ret = -ENOMEM;
+ if (!vma)
+ goto out_unlock;
+
+@@ -864,6 +870,7 @@ static int userfaultfd_register(struct u
+ } while (vma && vma->vm_start < end);
+ out_unlock:
+ up_write(&mm->mmap_sem);
++ mmput(mm);
+ if (!ret) {
+ /*
+ * Now that we scanned all vmas we can already tell
+@@ -902,10 +909,12 @@ static int userfaultfd_unregister(struct
+ start = uffdio_unregister.start;
+ end = start + uffdio_unregister.len;
+
++ ret = -ENOMEM;
++ if (!mmget_not_zero(mm))
++ goto out;
++
+ down_write(&mm->mmap_sem);
+ vma = find_vma_prev(mm, start, &prev);
+-
+- ret = -ENOMEM;
+ if (!vma)
+ goto out_unlock;
+
+@@ -998,6 +1007,7 @@ static int userfaultfd_unregister(struct
+ } while (vma && vma->vm_start < end);
+ out_unlock:
+ up_write(&mm->mmap_sem);
++ mmput(mm);
+ out:
+ return ret;
+ }
+@@ -1067,9 +1077,11 @@ static int userfaultfd_copy(struct userf
+ goto out;
+ if (uffdio_copy.mode & ~UFFDIO_COPY_MODE_DONTWAKE)
+ goto out;
+-
+- ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src,
+- uffdio_copy.len);
++ if (mmget_not_zero(ctx->mm)) {
++ ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src,
++ uffdio_copy.len);
++ mmput(ctx->mm);
++ }
+ if (unlikely(put_user(ret, &user_uffdio_copy->copy)))
+ return -EFAULT;
+ if (ret < 0)
+@@ -1110,8 +1122,11 @@ static int userfaultfd_zeropage(struct u
+ if (uffdio_zeropage.mode & ~UFFDIO_ZEROPAGE_MODE_DONTWAKE)
+ goto out;
+
+- ret = mfill_zeropage(ctx->mm, uffdio_zeropage.range.start,
+- uffdio_zeropage.range.len);
++ if (mmget_not_zero(ctx->mm)) {
++ ret = mfill_zeropage(ctx->mm, uffdio_zeropage.range.start,
++ uffdio_zeropage.range.len);
++ mmput(ctx->mm);
++ }
+ if (unlikely(put_user(ret, &user_uffdio_zeropage->zeropage)))
+ return -EFAULT;
+ if (ret < 0)
+@@ -1289,12 +1304,12 @@ static struct file *userfaultfd_file_cre
+ ctx->released = false;
+ ctx->mm = current->mm;
+ /* prevent the mm struct to be freed */
+- atomic_inc(&ctx->mm->mm_users);
++ atomic_inc(&ctx->mm->mm_count);
+
+ file = anon_inode_getfile("[userfaultfd]", &userfaultfd_fops, ctx,
+ O_RDWR | (flags & UFFD_SHARED_FCNTL_FLAGS));
+ if (IS_ERR(file)) {
+- mmput(ctx->mm);
++ mmdrop(ctx->mm);
+ kmem_cache_free(userfaultfd_ctx_cachep, ctx);
+ }
+ out:
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -2614,12 +2614,17 @@ extern struct mm_struct * mm_alloc(void)
+
+ /* mmdrop drops the mm and the page tables */
+ extern void __mmdrop(struct mm_struct *);
+-static inline void mmdrop(struct mm_struct * mm)
++static inline void mmdrop(struct mm_struct *mm)
+ {
+ if (unlikely(atomic_dec_and_test(&mm->mm_count)))
+ __mmdrop(mm);
+ }
+
++static inline bool mmget_not_zero(struct mm_struct *mm)
++{
++ return atomic_inc_not_zero(&mm->mm_users);
++}
++
+ /* mmput gets rid of the mappings and all user-space */
+ extern void mmput(struct mm_struct *);
+ /* Grab a reference to a task's mm, if it is not already going away */