From: Greg Kroah-Hartman Date: Tue, 4 Jun 2019 14:46:52 +0000 (+0200) Subject: 4.4-stable patches X-Git-Tag: v5.1.8~21 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fkernel%2Fstable-queue.git;a=commitdiff_plain;h=dd5b528c98e8f4a61ec7ee0b93e0b585d3eb89b4 4.4-stable patches added patches: binder-replace-p-with-pk-for-stable.patch binder-replace-p-with-pk.patch bnx2x-disable-gso-where-gso_size-is-too-big-for-hardware.patch brcmfmac-add-length-checks-in-scheduled-scan-result-handler.patch brcmfmac-add-length-checks-on-firmware-events.patch brcmfmac-add-subtype-check-for-event-handling-in-data-path.patch brcmfmac-fix-incorrect-event-channel-deduction.patch brcmfmac-revise-handling-events-in-receive-path.patch brcmfmac-screening-firmware-event-packet.patch coredump-fix-race-condition-between-mmget_not_zero-get_task_mm-and-core-dumping.patch net-create-skb_gso_validate_mac_len.patch userfaultfd-don-t-pin-the-user-memory-in-userfaultfd_file_create.patch --- diff --git a/queue-4.4/binder-replace-p-with-pk-for-stable.patch b/queue-4.4/binder-replace-p-with-pk-for-stable.patch new file mode 100644 index 0000000000..ca3e8f7aeb --- /dev/null +++ b/queue-4.4/binder-replace-p-with-pk-for-stable.patch @@ -0,0 +1,146 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Ben Hutchings +Date: Wed, 29 May 2019 18:02:44 +0100 +Subject: binder: Replace "%p" with "%pK" for stable + +From: Ben Hutchings + +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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); + } diff --git a/queue-4.4/binder-replace-p-with-pk.patch b/queue-4.4/binder-replace-p-with-pk.patch new file mode 100644 index 0000000000..bf0a163f65 --- /dev/null +++ b/queue-4.4/binder-replace-p-with-pk.patch @@ -0,0 +1,59 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Todd Kjos +Date: Wed, 7 Feb 2018 13:57:37 -0800 +Subject: binder: replace "%p" with "%pK" + +From: Todd Kjos + +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 +Signed-off-by: Greg Kroah-Hartman +[bwh: Backported to 4.4: adjust context] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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); + } diff --git a/queue-4.4/bnx2x-disable-gso-where-gso_size-is-too-big-for-hardware.patch b/queue-4.4/bnx2x-disable-gso-where-gso_size-is-too-big-for-hardware.patch new file mode 100644 index 0000000000..61aea25e54 --- /dev/null +++ b/queue-4.4/bnx2x-disable-gso-where-gso_size-is-too-big-for-hardware.patch @@ -0,0 +1,58 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Daniel Axtens +Date: Wed, 31 Jan 2018 14:15:34 +1100 +Subject: bnx2x: disable GSO where gso_size is too big for hardware + +From: Daniel Axtens + +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 +Reviewed-by: Eric Dumazet +Signed-off-by: David S. Miller +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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); + } diff --git a/queue-4.4/brcmfmac-add-length-checks-in-scheduled-scan-result-handler.patch b/queue-4.4/brcmfmac-add-length-checks-in-scheduled-scan-result-handler.patch new file mode 100644 index 0000000000..c061bfc888 --- /dev/null +++ b/queue-4.4/brcmfmac-add-length-checks-in-scheduled-scan-result-handler.patch @@ -0,0 +1,72 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Arend Van Spriel +Date: Thu, 6 Apr 2017 13:14:40 +0100 +Subject: brcmfmac: add length checks in scheduled scan result handler + +From: Arend Van Spriel + +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 +Reviewed-by: Pieter-Paul Giesberts +Reviewed-by: Franky Lin +Signed-off-by: Arend van Spriel +Signed-off-by: Kalle Valo +[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 +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/queue-4.4/brcmfmac-add-length-checks-on-firmware-events.patch b/queue-4.4/brcmfmac-add-length-checks-on-firmware-events.patch new file mode 100644 index 0000000000..ebc84e14d0 --- /dev/null +++ b/queue-4.4/brcmfmac-add-length-checks-on-firmware-events.patch @@ -0,0 +1,286 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Hante Meuleman +Date: Wed, 17 Feb 2016 11:26:54 +0100 +Subject: brcmfmac: Add length checks on firmware events + +From: Hante Meuleman + +commit 0aedbcaf6f182690790d98d90d5fe1e64c846c34 upstream. + +Add additional length checks on firmware events to create more +robust code. + +Reviewed-by: Arend Van Spriel +Reviewed-by: Franky (Zhenhui) Lin +Reviewed-by: Pieter-Paul Giesberts +Reviewed-by: Lei Zhang +Signed-off-by: Hante Meuleman +Signed-off-by: Arend van Spriel +Signed-off-by: Kalle Valo +[bwh: Backported to 4.4: + - Drop changes to brcmf_wowl_nd_results() + - Adjust filenames] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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); + diff --git a/queue-4.4/brcmfmac-add-subtype-check-for-event-handling-in-data-path.patch b/queue-4.4/brcmfmac-add-subtype-check-for-event-handling-in-data-path.patch new file mode 100644 index 0000000000..d51292bb4a --- /dev/null +++ b/queue-4.4/brcmfmac-add-subtype-check-for-event-handling-in-data-path.patch @@ -0,0 +1,103 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Arend van Spriel +Date: Thu, 14 Feb 2019 13:43:48 +0100 +Subject: brcmfmac: add subtype check for event handling in data path + +From: Arend van Spriel + +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 +Reviewed-by: Pieter-Paul Giesberts +Reviewed-by: Franky Lin +Signed-off-by: Arend van Spriel +Signed-off-by: Kalle Valo +[bwh: Backported to 4.4: adjust filenames] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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); diff --git a/queue-4.4/brcmfmac-fix-incorrect-event-channel-deduction.patch b/queue-4.4/brcmfmac-fix-incorrect-event-channel-deduction.patch new file mode 100644 index 0000000000..ca63e6c531 --- /dev/null +++ b/queue-4.4/brcmfmac-fix-incorrect-event-channel-deduction.patch @@ -0,0 +1,40 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Gavin Li +Date: Tue, 17 Jan 2017 15:24:05 -0800 +Subject: brcmfmac: fix incorrect event channel deduction + +From: Gavin Li + +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 +Acked-by: Arend van Spriel +[kvalo@codeaurora.org: improve commit logs based on email discussion] +Signed-off-by: Kalle Valo +[bwh: Backported to 4.4: adjust filename] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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, diff --git a/queue-4.4/brcmfmac-revise-handling-events-in-receive-path.patch b/queue-4.4/brcmfmac-revise-handling-events-in-receive-path.patch new file mode 100644 index 0000000000..b39658fbaf --- /dev/null +++ b/queue-4.4/brcmfmac-revise-handling-events-in-receive-path.patch @@ -0,0 +1,149 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Arend van Spriel +Date: Mon, 11 Apr 2016 11:35:27 +0200 +Subject: brcmfmac: revise handling events in receive path + +From: Arend van Spriel + +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 +Reviewed-by: Pieter-Paul Giesberts +Reviewed-by: Franky Lin +Signed-off-by: Arend van Spriel +Signed-off-by: Kalle Valo +[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 +Signed-off-by: Greg Kroah-Hartman +--- + 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); + } + + diff --git a/queue-4.4/brcmfmac-screening-firmware-event-packet.patch b/queue-4.4/brcmfmac-screening-firmware-event-packet.patch new file mode 100644 index 0000000000..6c5006d629 --- /dev/null +++ b/queue-4.4/brcmfmac-screening-firmware-event-packet.patch @@ -0,0 +1,312 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Franky Lin +Date: Mon, 11 Apr 2016 11:35:25 +0200 +Subject: brcmfmac: screening firmware event packet + +From: Franky Lin + +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 +Signed-off-by: Franky Lin +Signed-off-by: Arend van Spriel +Signed-off-by: Kalle Valo +[bwh: Backported to 4.4 adjust filenames] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include ++#include + + #include + #include +@@ -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); diff --git a/queue-4.4/coredump-fix-race-condition-between-mmget_not_zero-get_task_mm-and-core-dumping.patch b/queue-4.4/coredump-fix-race-condition-between-mmget_not_zero-get_task_mm-and-core-dumping.patch new file mode 100644 index 0000000000..f638b9edb4 --- /dev/null +++ b/queue-4.4/coredump-fix-race-condition-between-mmget_not_zero-get_task_mm-and-core-dumping.patch @@ -0,0 +1,208 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Andrea Arcangeli +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 + +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 +Reported-by: Jann Horn +Suggested-by: Oleg Nesterov +Acked-by: Peter Xu +Reviewed-by: Mike Rapoport +Reviewed-by: Oleg Nesterov +Reviewed-by: Jann Horn +Acked-by: Jason Gunthorpe +Acked-by: Michal Hocko +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +[bwh: Backported to 4.4: + - Drop changes in Infiniband and userfaultfd_event_wait_completion() + - Adjust filename, context] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + #include ++#include + + #include + #include +@@ -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; diff --git a/queue-4.4/net-create-skb_gso_validate_mac_len.patch b/queue-4.4/net-create-skb_gso_validate_mac_len.patch new file mode 100644 index 0000000000..57919c003a --- /dev/null +++ b/queue-4.4/net-create-skb_gso_validate_mac_len.patch @@ -0,0 +1,86 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Daniel Axtens +Date: Wed, 31 Jan 2018 14:15:33 +1100 +Subject: net: create skb_gso_validate_mac_len() + +From: Daniel Axtens + +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 +Signed-off-by: David S. Miller +[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 + .] +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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 + */ diff --git a/queue-4.4/series b/queue-4.4/series index 4ed1ce99d2..c6923a7f41 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -212,3 +212,15 @@ memcg-make-it-work-on-sparse-non-0-node-systems.patch 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 diff --git a/queue-4.4/userfaultfd-don-t-pin-the-user-memory-in-userfaultfd_file_create.patch b/queue-4.4/userfaultfd-don-t-pin-the-user-memory-in-userfaultfd_file_create.patch new file mode 100644 index 0000000000..23a5d3a6d7 --- /dev/null +++ b/queue-4.4/userfaultfd-don-t-pin-the-user-memory-in-userfaultfd_file_create.patch @@ -0,0 +1,177 @@ +From foo@baz Tue 04 Jun 2019 04:46:27 PM CEST +From: Oleg Nesterov +Date: Fri, 20 May 2016 16:58:36 -0700 +Subject: userfaultfd: don't pin the user memory in userfaultfd_file_create() + +From: Oleg Nesterov + +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 +Cc: Andrea Arcangeli +Cc: Michal Hocko +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Ben Hutchings +Signed-off-by: Greg Kroah-Hartman +--- + 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 */