e45288b63b6c7fbea162a645e86fb3cead04218d
[openwrt/staging/adrian.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,