e45288b63b6c7fbea162a645e86fb3cead04218d
[oweals/openwrt.git] /
1 From 5cdb0ef6144f47440850553579aa923c20a63f23 Mon Sep 17 00:00:00 2001
2 From: Piotr Figiel <p.figiel@camlintechnologies.com>
3 Date: Mon, 4 Mar 2019 15:42:52 +0000
4 Subject: [PATCH] brcmfmac: fix NULL pointer derefence during USB disconnect
5
6 In case USB disconnect happens at the moment transmitting workqueue is in
7 progress the underlying interface may be gone causing a NULL pointer
8 dereference. Add synchronization of the workqueue destruction with the
9 detach implementation in core so that the transmitting workqueue is stopped
10 during detach before the interfaces are removed.
11
12 Fix following Oops:
13
14 Unable to handle kernel NULL pointer dereference at virtual address 00000008
15 pgd = 9e6a802d
16 [00000008] *pgd=00000000
17 Internal error: Oops: 5 [#1] PREEMPT SMP ARM
18 Modules linked in: nf_log_ipv4 nf_log_common xt_LOG xt_limit iptable_mangle
19 xt_connmark xt_tcpudp xt_conntrack nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4
20 iptable_filter ip_tables x_tables usb_f_mass_storage usb_f_rndis u_ether
21 usb_serial_simple usbserial cdc_acm brcmfmac brcmutil smsc95xx usbnet
22 ci_hdrc_imx ci_hdrc ulpi usbmisc_imx 8250_exar 8250_pci 8250 8250_base
23 libcomposite configfs udc_core
24 CPU: 0 PID: 7 Comm: kworker/u8:0 Not tainted 4.19.23-00076-g03740aa-dirty #102
25 Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
26 Workqueue: brcmf_fws_wq brcmf_fws_dequeue_worker [brcmfmac]
27 PC is at brcmf_txfinalize+0x34/0x90 [brcmfmac]
28 LR is at brcmf_fws_dequeue_worker+0x218/0x33c [brcmfmac]
29 pc : [<7f0dee64>]    lr : [<7f0e4140>]    psr: 60010093
30 sp : ee8abef0  ip : 00000000  fp : edf38000
31 r10: ffffffed  r9 : edf38970  r8 : edf38004
32 r7 : edf3e970  r6 : 00000000  r5 : ede69000  r4 : 00000000
33 r3 : 00000a97  r2 : 00000000  r1 : 0000888e  r0 : ede69000
34 Flags: nZCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment none
35 Control: 10c5387d  Table: 7d03c04a  DAC: 00000051
36 Process kworker/u8:0 (pid: 7, stack limit = 0x24ec3e04)
37 Stack: (0xee8abef0 to 0xee8ac000)
38 bee0:                                     ede69000 00000000 ed56c3e0 7f0e4140
39 bf00: 00000001 00000000 edf38004 edf3e99c ed56c3e0 80d03d00 edfea43a edf3e970
40 bf20: ee809880 ee804200 ee971100 00000000 edf3e974 00000000 ee804200 80135a70
41 bf40: 80d03d00 ee804218 ee809880 ee809894 ee804200 80d03d00 ee804218 ee8aa000
42 bf60: 00000088 80135d5c 00000000 ee829f00 ee829dc0 00000000 ee809880 80135d30
43 bf80: ee829f1c ee873eac 00000000 8013b1a0 ee829dc0 8013b07c 00000000 00000000
44 bfa0: 00000000 00000000 00000000 801010e8 00000000 00000000 00000000 00000000
45 bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
46 bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
47 [<7f0dee64>] (brcmf_txfinalize [brcmfmac]) from [<7f0e4140>] (brcmf_fws_dequeue_worker+0x218/0x33c [brcmfmac])
48 [<7f0e4140>] (brcmf_fws_dequeue_worker [brcmfmac]) from [<80135a70>] (process_one_work+0x138/0x3f8)
49 [<80135a70>] (process_one_work) from [<80135d5c>] (worker_thread+0x2c/0x554)
50 [<80135d5c>] (worker_thread) from [<8013b1a0>] (kthread+0x124/0x154)
51 [<8013b1a0>] (kthread) from [<801010e8>] (ret_from_fork+0x14/0x2c)
52 Exception stack(0xee8abfb0 to 0xee8abff8)
53 bfa0:                                     00000000 00000000 00000000 00000000
54 bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
55 bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
56 Code: e1530001 0a000007 e3560000 e1a00005 (05942008)
57 ---[ end trace 079239dd31c86e90 ]---
58
59 Signed-off-by: Piotr Figiel <p.figiel@camlintechnologies.com>
60 Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
61 ---
62  .../wireless/broadcom/brcm80211/brcmfmac/bcdc.c  | 11 +++++++++--
63  .../wireless/broadcom/brcm80211/brcmfmac/bcdc.h  |  6 ++++--
64  .../wireless/broadcom/brcm80211/brcmfmac/core.c  |  4 +++-
65  .../broadcom/brcm80211/brcmfmac/fwsignal.c       | 16 ++++++++++++----
66  .../broadcom/brcm80211/brcmfmac/fwsignal.h       |  3 ++-
67  .../wireless/broadcom/brcm80211/brcmfmac/proto.c | 10 ++++++++--
68  .../wireless/broadcom/brcm80211/brcmfmac/proto.h |  3 ++-
69  7 files changed, 40 insertions(+), 13 deletions(-)
70
71 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
72 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.c
73 @@ -490,11 +490,18 @@ fail:
74         return -ENOMEM;
75  }
76  
77 -void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr)
78 +void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr)
79 +{
80 +       struct brcmf_bcdc *bcdc = drvr->proto->pd;
81 +
82 +       brcmf_fws_detach_pre_delif(bcdc->fws);
83 +}
84 +
85 +void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr)
86  {
87         struct brcmf_bcdc *bcdc = drvr->proto->pd;
88  
89         drvr->proto->pd = NULL;
90 -       brcmf_fws_detach(bcdc->fws);
91 +       brcmf_fws_detach_post_delif(bcdc->fws);
92         kfree(bcdc);
93  }
94 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
95 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcdc.h
96 @@ -18,14 +18,16 @@
97  
98  #ifdef CPTCFG_BRCMFMAC_PROTO_BCDC
99  int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr);
100 -void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr);
101 +void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr);
102 +void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr);
103  void brcmf_proto_bcdc_txflowblock(struct device *dev, bool state);
104  void brcmf_proto_bcdc_txcomplete(struct device *dev, struct sk_buff *txp,
105                                  bool success);
106  struct brcmf_fws_info *drvr_to_fws(struct brcmf_pub *drvr);
107  #else
108  static inline int brcmf_proto_bcdc_attach(struct brcmf_pub *drvr) { return 0; }
109 -static inline void brcmf_proto_bcdc_detach(struct brcmf_pub *drvr) {}
110 +static void brcmf_proto_bcdc_detach_pre_delif(struct brcmf_pub *drvr) {};
111 +static inline void brcmf_proto_bcdc_detach_post_delif(struct brcmf_pub *drvr) {}
112  #endif
113  
114  #endif /* BRCMFMAC_BCDC_H */
115 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
116 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
117 @@ -1342,6 +1342,8 @@ void brcmf_detach(struct device *dev)
118  
119         brcmf_bus_change_state(bus_if, BRCMF_BUS_DOWN);
120  
121 +       brcmf_proto_detach_pre_delif(drvr);
122 +
123         /* make sure primary interface removed last */
124         for (i = BRCMF_MAX_IFS-1; i > -1; i--)
125                 brcmf_remove_interface(drvr->iflist[i], false);
126 @@ -1351,7 +1353,7 @@ void brcmf_detach(struct device *dev)
127  
128         brcmf_bus_stop(drvr->bus_if);
129  
130 -       brcmf_proto_detach(drvr);
131 +       brcmf_proto_detach_post_delif(drvr);
132  
133         bus_if->drvr = NULL;
134         wiphy_free(drvr->wiphy);
135 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
136 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.c
137 @@ -2443,17 +2443,25 @@ struct brcmf_fws_info *brcmf_fws_attach(
138         return fws;
139  
140  fail:
141 -       brcmf_fws_detach(fws);
142 +       brcmf_fws_detach_pre_delif(fws);
143 +       brcmf_fws_detach_post_delif(fws);
144         return ERR_PTR(rc);
145  }
146  
147 -void brcmf_fws_detach(struct brcmf_fws_info *fws)
148 +void brcmf_fws_detach_pre_delif(struct brcmf_fws_info *fws)
149  {
150         if (!fws)
151                 return;
152 -
153 -       if (fws->fws_wq)
154 +       if (fws->fws_wq) {
155                 destroy_workqueue(fws->fws_wq);
156 +               fws->fws_wq = NULL;
157 +       }
158 +}
159 +
160 +void brcmf_fws_detach_post_delif(struct brcmf_fws_info *fws)
161 +{
162 +       if (!fws)
163 +               return;
164  
165         /* cleanup */
166         brcmf_fws_lock(fws);
167 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
168 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwsignal.h
169 @@ -19,7 +19,8 @@
170  #define FWSIGNAL_H_
171  
172  struct brcmf_fws_info *brcmf_fws_attach(struct brcmf_pub *drvr);
173 -void brcmf_fws_detach(struct brcmf_fws_info *fws);
174 +void brcmf_fws_detach_pre_delif(struct brcmf_fws_info *fws);
175 +void brcmf_fws_detach_post_delif(struct brcmf_fws_info *fws);
176  void brcmf_fws_debugfs_create(struct brcmf_pub *drvr);
177  bool brcmf_fws_queue_skbs(struct brcmf_fws_info *fws);
178  bool brcmf_fws_fc_active(struct brcmf_fws_info *fws);
179 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
180 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.c
181 @@ -67,16 +67,22 @@ fail:
182         return -ENOMEM;
183  }
184  
185 -void brcmf_proto_detach(struct brcmf_pub *drvr)
186 +void brcmf_proto_detach_post_delif(struct brcmf_pub *drvr)
187  {
188         brcmf_dbg(TRACE, "Enter\n");
189  
190         if (drvr->proto) {
191                 if (drvr->bus_if->proto_type == BRCMF_PROTO_BCDC)
192 -                       brcmf_proto_bcdc_detach(drvr);
193 +                       brcmf_proto_bcdc_detach_post_delif(drvr);
194                 else if (drvr->bus_if->proto_type == BRCMF_PROTO_MSGBUF)
195                         brcmf_proto_msgbuf_detach(drvr);
196                 kfree(drvr->proto);
197                 drvr->proto = NULL;
198         }
199  }
200 +
201 +void brcmf_proto_detach_pre_delif(struct brcmf_pub *drvr)
202 +{
203 +       if (drvr->proto && drvr->bus_if->proto_type == BRCMF_PROTO_BCDC)
204 +               brcmf_proto_bcdc_detach_pre_delif(drvr);
205 +}
206 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
207 +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/proto.h
208 @@ -54,7 +54,8 @@ struct brcmf_proto {
209  
210  
211  int brcmf_proto_attach(struct brcmf_pub *drvr);
212 -void brcmf_proto_detach(struct brcmf_pub *drvr);
213 +void brcmf_proto_detach_pre_delif(struct brcmf_pub *drvr);
214 +void brcmf_proto_detach_post_delif(struct brcmf_pub *drvr);
215  
216  static inline int brcmf_proto_hdrpull(struct brcmf_pub *drvr, bool do_fws,
217                                       struct sk_buff *skb,