| From 5cdb0ef6144f47440850553579aa923c20a63f23 Mon Sep 17 00:00:00 2001 |
| From: Piotr Figiel <p.figiel@camlintechnologies.com> |
| Date: Mon, 4 Mar 2019 15:42:52 +0000 |
| Subject: brcmfmac: fix NULL pointer derefence during USB disconnect |
| |
| From: Piotr Figiel <p.figiel@camlintechnologies.com> |
| |
| commit 5cdb0ef6144f47440850553579aa923c20a63f23 upstream. |
| |
| In case USB disconnect happens at the moment transmitting workqueue is in |
| progress the underlying interface may be gone causing a NULL pointer |
| dereference. Add synchronization of the workqueue destruction with the |
| detach implementation in core so that the transmitting workqueue is stopped |
| during detach before the interfaces are removed. |
| |
| Fix following Oops: |
| |
| Unable to handle kernel NULL pointer dereference at virtual address 00000008 |
| pgd = 9e6a802d |
| [00000008] *pgd=00000000 |
| Internal error: Oops: 5 [#1] PREEMPT SMP ARM |
| Modules linked in: nf_log_ipv4 nf_log_common xt_LOG xt_limit iptable_mangle |
| xt_connmark xt_tcpudp xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 |
| iptable_filter ip_tables x_tables usb_f_mass_storage usb_f_rndis u_ether |
| usb_serial_simple usbserial cdc_acm brcmfmac brcmutil smsc95xx usbnet |
| ci_hdrc_imx ci_hdrc ulpi usbmisc_imx 8250_exar 8250_pci 8250 8250_base |
| libcomposite configfs udc_core |
| CPU: 0 PID: 7 Comm: kworker/u8:0 Not tainted 4.19.23-00076-g03740aa-dirty #102 |
| Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree) |
| Workqueue: brcmf_fws_wq brcmf_fws_dequeue_worker [brcmfmac] |
| PC is at brcmf_txfinalize+0x34/0x90 [brcmfmac] |
| LR is at brcmf_fws_dequeue_worker+0x218/0x33c [brcmfmac] |
| pc : [<7f0dee64>] lr : [<7f0e4140>] psr: 60010093 |
| sp : ee8abef0 ip : 00000000 fp : edf38000 |
| r10: ffffffed r9 : edf38970 r8 : edf38004 |
| r7 : edf3e970 r6 : 00000000 r5 : ede69000 r4 : 00000000 |
| r3 : 00000a97 r2 : 00000000 r1 : 0000888e r0 : ede69000 |
| Flags: nZCv IRQs off FIQs on Mode SVC_32 ISA ARM Segment none |
| Control: 10c5387d Table: 7d03c04a DAC: 00000051 |
| Process kworker/u8:0 (pid: 7, stack limit = 0x24ec3e04) |
| Stack: (0xee8abef0 to 0xee8ac000) |
| bee0: ede69000 00000000 ed56c3e0 7f0e4140 |
| bf00: 00000001 00000000 edf38004 edf3e99c ed56c3e0 80d03d00 edfea43a edf3e970 |
| bf20: ee809880 ee804200 ee971100 00000000 edf3e974 00000000 ee804200 80135a70 |
| bf40: 80d03d00 ee804218 ee809880 ee809894 ee804200 80d03d00 ee804218 ee8aa000 |
| bf60: 00000088 80135d5c 00000000 ee829f00 ee829dc0 00000000 ee809880 80135d30 |
| bf80: ee829f1c ee873eac 00000000 8013b1a0 ee829dc0 8013b07c 00000000 00000000 |
| bfa0: 00000000 00000000 00000000 801010e8 00000000 00000000 00000000 00000000 |
| bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 |
| bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000 |
| [<7f0dee64>] (brcmf_txfinalize [brcmfmac]) from [<7f0e4140>] (brcmf_fws_dequeue_worker+0x218/0x33c [brcmfmac]) |
| [<7f0e4140>] (brcmf_fws_dequeue_worker [brcmfmac]) from [<80135a70>] (process_one_work+0x138/0x3f8) |
| [<80135a70>] (process_one_work) from [<80135d5c>] (worker_thread+0x2c/0x554) |
| [<80135d5c>] (worker_thread) from [<8013b1a0>] (kthread+0x124/0x154) |
| [<8013b1a0>] (kthread) from [<801010e8>] (ret_from_fork+0x14/0x2c) |
| Exception stack(0xee8abfb0 to 0xee8abff8) |
| bfa0: 00000000 00000000 00000000 00000000 |
| bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 |
| bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 |
| Code: e1530001 0a000007 e3560000 e1a00005 (05942008) |
| ---[ end trace 079239dd31c86e90 ]--- |
| |
| Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com> |
| Signed-off-by: Kalle Valo <kvalo@codeaurora.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c | 11 ++++++-- |
| drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h | 6 +++- |
| drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c | 4 ++- |
| drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c | 16 +++++++++--- |
| drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h | 3 +- |
| drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c | 10 ++++++- |
| drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h | 3 +- |
| 7 files changed, 40 insertions(+), 13 deletions(-) |
| |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c |
| @@ -490,11 +490,18 @@ fail: |
| return -ENOMEM; |
| } |
| |
| -void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) |
| +void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr) |
| +{ |
| + struct brcmf_bcdc *bcdc = drvr->proto->pd; |
| + |
| + brcmf_fws_detach_pre_delif(bcdc->fws); |
| +} |
| + |
| +void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr) |
| { |
| struct brcmf_bcdc *bcdc = drvr->proto->pd; |
| |
| drvr->proto->pd = NULL; |
| - brcmf_fws_detach(bcdc->fws); |
| + brcmf_fws_detach_post_delif(bcdc->fws); |
| kfree(bcdc); |
| } |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h |
| @@ -18,14 +18,16 @@ |
| |
| #ifdef CONFIG_BRCMFMAC_PROTO_BCDC |
| int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr); |
| -void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr); |
| +void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr); |
| +void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr); |
| void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state); |
| void brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp, |
| bool success); |
| struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr); |
| #else |
| static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; } |
| -static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {} |
| +static void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr) {}; |
| +static inline void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr) {} |
| #endif |
| |
| #endif /* BRCMFMAC_BCDC_H */ |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c |
| @@ -1244,6 +1244,8 @@ void brcmf_detach(struct device *dev) |
| |
| brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN); |
| |
| + brcmf_proto_detach_pre_delif(drvr); |
| + |
| /* make sure primary interface removed last */ |
| for (i = BRCMF_MAX_IFS-1; i > -1; i--) |
| brcmf_remove_interface(drvr->iflist[i], false); |
| @@ -1253,7 +1255,7 @@ void brcmf_detach(struct device *dev) |
| |
| brcmf_bus_stop(drvr->bus_if); |
| |
| - brcmf_proto_detach(drvr); |
| + brcmf_proto_detach_post_delif(drvr); |
| |
| bus_if->drvr = NULL; |
| wiphy_free(drvr->wiphy); |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c |
| @@ -2410,17 +2410,25 @@ struct brcmf_fws_info *brcmf_fws_attach( |
| return fws; |
| |
| fail: |
| - brcmf_fws_detach(fws); |
| + brcmf_fws_detach_pre_delif(fws); |
| + brcmf_fws_detach_post_delif(fws); |
| return ERR_PTR(rc); |
| } |
| |
| -void brcmf_fws_detach(struct brcmf_fws_info *fws) |
| +void brcmf_fws_detach_pre_delif(struct brcmf_fws_info *fws) |
| { |
| if (!fws) |
| return; |
| - |
| - if (fws->fws_wq) |
| + if (fws->fws_wq) { |
| destroy_workqueue(fws->fws_wq); |
| + fws->fws_wq = NULL; |
| + } |
| +} |
| + |
| +void brcmf_fws_detach_post_delif(struct brcmf_fws_info *fws) |
| +{ |
| + if (!fws) |
| + return; |
| |
| /* cleanup */ |
| brcmf_fws_lock(fws); |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h |
| @@ -19,7 +19,8 @@ |
| #define FWSIGNAL_H_ |
| |
| struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr); |
| -void brcmf_fws_detach(struct brcmf_fws_info *fws); |
| +void brcmf_fws_detach_pre_delif(struct brcmf_fws_info *fws); |
| +void brcmf_fws_detach_post_delif(struct brcmf_fws_info *fws); |
| void brcmf_fws_debugfs_create(struct brcmf_pub *drvr); |
| bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws); |
| bool brcmf_fws_fc_active(struct brcmf_fws_info *fws); |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c |
| @@ -67,16 +67,22 @@ fail: |
| return -ENOMEM; |
| } |
| |
| -void brcmf_proto_detach(struct brcmf_pub *drvr) |
| +void brcmf_proto_detach_post_delif(struct brcmf_pub *drvr) |
| { |
| brcmf_dbg(TRACE, "Enter\n"); |
| |
| if (drvr->proto) { |
| if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) |
| - brcmf_proto_bcdc_detach(drvr); |
| + brcmf_proto_bcdc_detach_post_delif(drvr); |
| else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF) |
| brcmf_proto_msgbuf_detach(drvr); |
| kfree(drvr->proto); |
| drvr->proto = NULL; |
| } |
| } |
| + |
| +void brcmf_proto_detach_pre_delif(struct brcmf_pub *drvr) |
| +{ |
| + if (drvr->proto && drvr->bus_if->proto_type == BRCMF_PROTO_BCDC) |
| + brcmf_proto_bcdc_detach_pre_delif(drvr); |
| +} |
| --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h |
| +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h |
| @@ -54,7 +54,8 @@ struct brcmf_proto { |
| |
| |
| int brcmf_proto_attach(struct brcmf_pub *drvr); |
| -void brcmf_proto_detach(struct brcmf_pub *drvr); |
| +void brcmf_proto_detach_pre_delif(struct brcmf_pub *drvr); |
| +void brcmf_proto_detach_post_delif(struct brcmf_pub *drvr); |
| |
| static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws, |
| struct sk_buff *skb, |