diff options
author | Christian Lamparter <chunkeey@web.de> | 2008-12-09 09:14:37 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-12-12 14:01:39 -0500 |
commit | dd397dc9dddfa2149a1bbc9e52ac7d5630737cec (patch) | |
tree | e96caebf6081bbeb70bc8407342989c0bbda4fbf /drivers/net/wireless | |
parent | a07d3619faeea3f540dd55f86685136f8928b4ad (diff) |
p54usb: rewriting rx/tx routines to make use of usb_anchor's facilities
Alan Stern found several flaws in p54usb's implementation and annotated:
"usb_kill_urb() and similar routines do not expect an URB's completion
routine to deallocate it. This is almost obvious -- if the URB is deallocated
before the completion routine returns then there's no way for usb_kill_urb
to detect when the URB actually is complete."
This patch addresses all known limitations in the old implementation and fixes
khub's "use-after-freed" hang, when SLUB debug's poisoning option is enabled.
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Cc: stable@kernel.org
Tested-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/p54/p54usb.c | 151 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54usb.h | 1 |
2 files changed, 102 insertions, 50 deletions
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 2dd3cd41d0fe..c2789e53b984 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c | |||
@@ -86,13 +86,13 @@ static void p54u_rx_cb(struct urb *urb) | |||
86 | struct ieee80211_hw *dev = info->dev; | 86 | struct ieee80211_hw *dev = info->dev; |
87 | struct p54u_priv *priv = dev->priv; | 87 | struct p54u_priv *priv = dev->priv; |
88 | 88 | ||
89 | skb_unlink(skb, &priv->rx_queue); | ||
90 | |||
89 | if (unlikely(urb->status)) { | 91 | if (unlikely(urb->status)) { |
90 | info->urb = NULL; | 92 | dev_kfree_skb_irq(skb); |
91 | usb_free_urb(urb); | ||
92 | return; | 93 | return; |
93 | } | 94 | } |
94 | 95 | ||
95 | skb_unlink(skb, &priv->rx_queue); | ||
96 | skb_put(skb, urb->actual_length); | 96 | skb_put(skb, urb->actual_length); |
97 | 97 | ||
98 | if (priv->hw_type == P54U_NET2280) | 98 | if (priv->hw_type == P54U_NET2280) |
@@ -105,7 +105,6 @@ static void p54u_rx_cb(struct urb *urb) | |||
105 | if (p54_rx(dev, skb)) { | 105 | if (p54_rx(dev, skb)) { |
106 | skb = dev_alloc_skb(priv->common.rx_mtu + 32); | 106 | skb = dev_alloc_skb(priv->common.rx_mtu + 32); |
107 | if (unlikely(!skb)) { | 107 | if (unlikely(!skb)) { |
108 | usb_free_urb(urb); | ||
109 | /* TODO check rx queue length and refill *somewhere* */ | 108 | /* TODO check rx queue length and refill *somewhere* */ |
110 | return; | 109 | return; |
111 | } | 110 | } |
@@ -115,7 +114,6 @@ static void p54u_rx_cb(struct urb *urb) | |||
115 | info->dev = dev; | 114 | info->dev = dev; |
116 | urb->transfer_buffer = skb_tail_pointer(skb); | 115 | urb->transfer_buffer = skb_tail_pointer(skb); |
117 | urb->context = skb; | 116 | urb->context = skb; |
118 | skb_queue_tail(&priv->rx_queue, skb); | ||
119 | } else { | 117 | } else { |
120 | if (priv->hw_type == P54U_NET2280) | 118 | if (priv->hw_type == P54U_NET2280) |
121 | skb_push(skb, priv->common.tx_hdr_len); | 119 | skb_push(skb, priv->common.tx_hdr_len); |
@@ -130,11 +128,14 @@ static void p54u_rx_cb(struct urb *urb) | |||
130 | WARN_ON(1); | 128 | WARN_ON(1); |
131 | urb->transfer_buffer = skb_tail_pointer(skb); | 129 | urb->transfer_buffer = skb_tail_pointer(skb); |
132 | } | 130 | } |
133 | |||
134 | skb_queue_tail(&priv->rx_queue, skb); | ||
135 | } | 131 | } |
136 | 132 | skb_queue_tail(&priv->rx_queue, skb); | |
137 | usb_submit_urb(urb, GFP_ATOMIC); | 133 | usb_anchor_urb(urb, &priv->submitted); |
134 | if (usb_submit_urb(urb, GFP_ATOMIC)) { | ||
135 | skb_unlink(skb, &priv->rx_queue); | ||
136 | usb_unanchor_urb(urb); | ||
137 | dev_kfree_skb_irq(skb); | ||
138 | } | ||
138 | } | 139 | } |
139 | 140 | ||
140 | static void p54u_tx_reuse_skb_cb(struct urb *urb) | 141 | static void p54u_tx_reuse_skb_cb(struct urb *urb) |
@@ -144,18 +145,6 @@ static void p54u_tx_reuse_skb_cb(struct urb *urb) | |||
144 | usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)))->priv; | 145 | usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)))->priv; |
145 | 146 | ||
146 | skb_pull(skb, priv->common.tx_hdr_len); | 147 | skb_pull(skb, priv->common.tx_hdr_len); |
147 | usb_free_urb(urb); | ||
148 | } | ||
149 | |||
150 | static void p54u_tx_cb(struct urb *urb) | ||
151 | { | ||
152 | usb_free_urb(urb); | ||
153 | } | ||
154 | |||
155 | static void p54u_tx_free_cb(struct urb *urb) | ||
156 | { | ||
157 | kfree(urb->transfer_buffer); | ||
158 | usb_free_urb(urb); | ||
159 | } | 148 | } |
160 | 149 | ||
161 | static void p54u_tx_free_skb_cb(struct urb *urb) | 150 | static void p54u_tx_free_skb_cb(struct urb *urb) |
@@ -165,25 +154,36 @@ static void p54u_tx_free_skb_cb(struct urb *urb) | |||
165 | usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); | 154 | usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); |
166 | 155 | ||
167 | p54_free_skb(dev, skb); | 156 | p54_free_skb(dev, skb); |
168 | usb_free_urb(urb); | 157 | } |
158 | |||
159 | static void p54u_tx_dummy_cb(struct urb *urb) { } | ||
160 | |||
161 | static void p54u_free_urbs(struct ieee80211_hw *dev) | ||
162 | { | ||
163 | struct p54u_priv *priv = dev->priv; | ||
164 | usb_kill_anchored_urbs(&priv->submitted); | ||
169 | } | 165 | } |
170 | 166 | ||
171 | static int p54u_init_urbs(struct ieee80211_hw *dev) | 167 | static int p54u_init_urbs(struct ieee80211_hw *dev) |
172 | { | 168 | { |
173 | struct p54u_priv *priv = dev->priv; | 169 | struct p54u_priv *priv = dev->priv; |
174 | struct urb *entry; | 170 | struct urb *entry = NULL; |
175 | struct sk_buff *skb; | 171 | struct sk_buff *skb; |
176 | struct p54u_rx_info *info; | 172 | struct p54u_rx_info *info; |
173 | int ret = 0; | ||
177 | 174 | ||
178 | while (skb_queue_len(&priv->rx_queue) < 32) { | 175 | while (skb_queue_len(&priv->rx_queue) < 32) { |
179 | skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); | 176 | skb = __dev_alloc_skb(priv->common.rx_mtu + 32, GFP_KERNEL); |
180 | if (!skb) | 177 | if (!skb) { |
181 | break; | 178 | ret = -ENOMEM; |
179 | goto err; | ||
180 | } | ||
182 | entry = usb_alloc_urb(0, GFP_KERNEL); | 181 | entry = usb_alloc_urb(0, GFP_KERNEL); |
183 | if (!entry) { | 182 | if (!entry) { |
184 | kfree_skb(skb); | 183 | ret = -ENOMEM; |
185 | break; | 184 | goto err; |
186 | } | 185 | } |
186 | |||
187 | usb_fill_bulk_urb(entry, priv->udev, | 187 | usb_fill_bulk_urb(entry, priv->udev, |
188 | usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), | 188 | usb_rcvbulkpipe(priv->udev, P54U_PIPE_DATA), |
189 | skb_tail_pointer(skb), | 189 | skb_tail_pointer(skb), |
@@ -192,26 +192,25 @@ static int p54u_init_urbs(struct ieee80211_hw *dev) | |||
192 | info->urb = entry; | 192 | info->urb = entry; |
193 | info->dev = dev; | 193 | info->dev = dev; |
194 | skb_queue_tail(&priv->rx_queue, skb); | 194 | skb_queue_tail(&priv->rx_queue, skb); |
195 | usb_submit_urb(entry, GFP_KERNEL); | 195 | |
196 | usb_anchor_urb(entry, &priv->submitted); | ||
197 | ret = usb_submit_urb(entry, GFP_KERNEL); | ||
198 | if (ret) { | ||
199 | skb_unlink(skb, &priv->rx_queue); | ||
200 | usb_unanchor_urb(entry); | ||
201 | goto err; | ||
202 | } | ||
203 | usb_free_urb(entry); | ||
204 | entry = NULL; | ||
196 | } | 205 | } |
197 | 206 | ||
198 | return 0; | 207 | return 0; |
199 | } | ||
200 | 208 | ||
201 | static void p54u_free_urbs(struct ieee80211_hw *dev) | 209 | err: |
202 | { | 210 | usb_free_urb(entry); |
203 | struct p54u_priv *priv = dev->priv; | 211 | kfree_skb(skb); |
204 | struct p54u_rx_info *info; | 212 | p54u_free_urbs(dev); |
205 | struct sk_buff *skb; | 213 | return ret; |
206 | |||
207 | while ((skb = skb_dequeue(&priv->rx_queue))) { | ||
208 | info = (struct p54u_rx_info *) skb->cb; | ||
209 | if (!info->urb) | ||
210 | continue; | ||
211 | |||
212 | usb_kill_urb(info->urb); | ||
213 | kfree_skb(skb); | ||
214 | } | ||
215 | } | 214 | } |
216 | 215 | ||
217 | static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb, | 216 | static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb, |
@@ -219,6 +218,7 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb, | |||
219 | { | 218 | { |
220 | struct p54u_priv *priv = dev->priv; | 219 | struct p54u_priv *priv = dev->priv; |
221 | struct urb *addr_urb, *data_urb; | 220 | struct urb *addr_urb, *data_urb; |
221 | int err = 0; | ||
222 | 222 | ||
223 | addr_urb = usb_alloc_urb(0, GFP_ATOMIC); | 223 | addr_urb = usb_alloc_urb(0, GFP_ATOMIC); |
224 | if (!addr_urb) | 224 | if (!addr_urb) |
@@ -233,15 +233,31 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb, | |||
233 | usb_fill_bulk_urb(addr_urb, priv->udev, | 233 | usb_fill_bulk_urb(addr_urb, priv->udev, |
234 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | 234 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), |
235 | &((struct p54_hdr *)skb->data)->req_id, 4, | 235 | &((struct p54_hdr *)skb->data)->req_id, 4, |
236 | p54u_tx_cb, dev); | 236 | p54u_tx_dummy_cb, dev); |
237 | usb_fill_bulk_urb(data_urb, priv->udev, | 237 | usb_fill_bulk_urb(data_urb, priv->udev, |
238 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | 238 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), |
239 | skb->data, skb->len, | 239 | skb->data, skb->len, |
240 | free_on_tx ? p54u_tx_free_skb_cb : | 240 | free_on_tx ? p54u_tx_free_skb_cb : |
241 | p54u_tx_reuse_skb_cb, skb); | 241 | p54u_tx_reuse_skb_cb, skb); |
242 | 242 | ||
243 | usb_submit_urb(addr_urb, GFP_ATOMIC); | 243 | usb_anchor_urb(addr_urb, &priv->submitted); |
244 | usb_submit_urb(data_urb, GFP_ATOMIC); | 244 | err = usb_submit_urb(addr_urb, GFP_ATOMIC); |
245 | if (err) { | ||
246 | usb_unanchor_urb(addr_urb); | ||
247 | goto out; | ||
248 | } | ||
249 | |||
250 | usb_anchor_urb(addr_urb, &priv->submitted); | ||
251 | err = usb_submit_urb(data_urb, GFP_ATOMIC); | ||
252 | if (err) | ||
253 | usb_unanchor_urb(data_urb); | ||
254 | |||
255 | out: | ||
256 | usb_free_urb(addr_urb); | ||
257 | usb_free_urb(data_urb); | ||
258 | |||
259 | if (err) | ||
260 | p54_free_skb(dev, skb); | ||
245 | } | 261 | } |
246 | 262 | ||
247 | static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) | 263 | static __le32 p54u_lm87_chksum(const __le32 *data, size_t length) |
@@ -281,7 +297,13 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb, | |||
281 | free_on_tx ? p54u_tx_free_skb_cb : | 297 | free_on_tx ? p54u_tx_free_skb_cb : |
282 | p54u_tx_reuse_skb_cb, skb); | 298 | p54u_tx_reuse_skb_cb, skb); |
283 | 299 | ||
284 | usb_submit_urb(data_urb, GFP_ATOMIC); | 300 | usb_anchor_urb(data_urb, &priv->submitted); |
301 | if (usb_submit_urb(data_urb, GFP_ATOMIC)) { | ||
302 | usb_unanchor_urb(data_urb); | ||
303 | skb_pull(skb, sizeof(*hdr)); | ||
304 | p54_free_skb(dev, skb); | ||
305 | } | ||
306 | usb_free_urb(data_urb); | ||
285 | } | 307 | } |
286 | 308 | ||
287 | static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb, | 309 | static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb, |
@@ -291,6 +313,7 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb, | |||
291 | struct urb *int_urb, *data_urb; | 313 | struct urb *int_urb, *data_urb; |
292 | struct net2280_tx_hdr *hdr; | 314 | struct net2280_tx_hdr *hdr; |
293 | struct net2280_reg_write *reg; | 315 | struct net2280_reg_write *reg; |
316 | int err = 0; | ||
294 | 317 | ||
295 | reg = kmalloc(sizeof(*reg), GFP_ATOMIC); | 318 | reg = kmalloc(sizeof(*reg), GFP_ATOMIC); |
296 | if (!reg) | 319 | if (!reg) |
@@ -320,15 +343,42 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb, | |||
320 | 343 | ||
321 | usb_fill_bulk_urb(int_urb, priv->udev, | 344 | usb_fill_bulk_urb(int_urb, priv->udev, |
322 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), | 345 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), |
323 | p54u_tx_free_cb, dev); | 346 | p54u_tx_dummy_cb, dev); |
324 | usb_submit_urb(int_urb, GFP_ATOMIC); | 347 | |
348 | /* | ||
349 | * This flag triggers a code path in the USB subsystem that will | ||
350 | * free what's inside the transfer_buffer after the callback routine | ||
351 | * has completed. | ||
352 | */ | ||
353 | int_urb->transfer_flags |= URB_FREE_BUFFER; | ||
325 | 354 | ||
326 | usb_fill_bulk_urb(data_urb, priv->udev, | 355 | usb_fill_bulk_urb(data_urb, priv->udev, |
327 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | 356 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), |
328 | skb->data, skb->len, | 357 | skb->data, skb->len, |
329 | free_on_tx ? p54u_tx_free_skb_cb : | 358 | free_on_tx ? p54u_tx_free_skb_cb : |
330 | p54u_tx_reuse_skb_cb, skb); | 359 | p54u_tx_reuse_skb_cb, skb); |
331 | usb_submit_urb(data_urb, GFP_ATOMIC); | 360 | |
361 | usb_anchor_urb(int_urb, &priv->submitted); | ||
362 | err = usb_submit_urb(int_urb, GFP_ATOMIC); | ||
363 | if (err) { | ||
364 | usb_unanchor_urb(int_urb); | ||
365 | goto out; | ||
366 | } | ||
367 | |||
368 | usb_anchor_urb(data_urb, &priv->submitted); | ||
369 | err = usb_submit_urb(data_urb, GFP_ATOMIC); | ||
370 | if (err) { | ||
371 | usb_unanchor_urb(data_urb); | ||
372 | goto out; | ||
373 | } | ||
374 | out: | ||
375 | usb_free_urb(int_urb); | ||
376 | usb_free_urb(data_urb); | ||
377 | |||
378 | if (err) { | ||
379 | skb_pull(skb, sizeof(*hdr)); | ||
380 | p54_free_skb(dev, skb); | ||
381 | } | ||
332 | } | 382 | } |
333 | 383 | ||
334 | static int p54u_write(struct p54u_priv *priv, | 384 | static int p54u_write(struct p54u_priv *priv, |
@@ -885,6 +935,7 @@ static int __devinit p54u_probe(struct usb_interface *intf, | |||
885 | goto err_free_dev; | 935 | goto err_free_dev; |
886 | 936 | ||
887 | skb_queue_head_init(&priv->rx_queue); | 937 | skb_queue_head_init(&priv->rx_queue); |
938 | init_usb_anchor(&priv->submitted); | ||
888 | 939 | ||
889 | p54u_open(dev); | 940 | p54u_open(dev); |
890 | err = p54_read_eeprom(dev); | 941 | err = p54_read_eeprom(dev); |
diff --git a/drivers/net/wireless/p54/p54usb.h b/drivers/net/wireless/p54/p54usb.h index 5b8fe91379c3..54ee738bf2af 100644 --- a/drivers/net/wireless/p54/p54usb.h +++ b/drivers/net/wireless/p54/p54usb.h | |||
@@ -133,6 +133,7 @@ struct p54u_priv { | |||
133 | 133 | ||
134 | spinlock_t lock; | 134 | spinlock_t lock; |
135 | struct sk_buff_head rx_queue; | 135 | struct sk_buff_head rx_queue; |
136 | struct usb_anchor submitted; | ||
136 | }; | 137 | }; |
137 | 138 | ||
138 | #endif /* P54USB_H */ | 139 | #endif /* P54USB_H */ |