diff options
author | Christian Lamparter <chunkeey@web.de> | 2009-01-19 18:27:57 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-22 13:55:02 -0500 |
commit | e2fe154e918276e900067a9d1d3a6a963faee041 (patch) | |
tree | 22936f2b3784c4d89c4f1e2263b528167f42e683 /drivers/net/wireless/p54/p54usb.c | |
parent | 12da401e0d616f738c8b8a368d1f63f365cc78e4 (diff) |
p54usb: fix nasty use after free
In theory, the firmware acks the received a data frame, before signaling the driver to free it again.
However Artur Skawina <art.08.09@gmail.com> has shown that it can happen in reverse order as well.
This is very bad and could lead to memory corruptions, oopses and panics.
Thanks to Artur Skawina <art.08.09@gmail.com> for reporting and debugging this issue.
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Tested-by: Artur Skawina <art.08.09@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/p54/p54usb.c')
-rw-r--r-- | drivers/net/wireless/p54/p54usb.c | 34 |
1 files changed, 13 insertions, 21 deletions
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 6a6a72f6f82c..4487cc5c928b 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c | |||
@@ -144,11 +144,8 @@ static void p54u_tx_cb(struct urb *urb) | |||
144 | struct sk_buff *skb = urb->context; | 144 | struct sk_buff *skb = urb->context; |
145 | struct ieee80211_hw *dev = (struct ieee80211_hw *) | 145 | struct ieee80211_hw *dev = (struct ieee80211_hw *) |
146 | usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); | 146 | usb_get_intfdata(usb_ifnum_to_if(urb->dev, 0)); |
147 | struct p54u_priv *priv = dev->priv; | ||
148 | 147 | ||
149 | skb_pull(skb, priv->common.tx_hdr_len); | 148 | p54_free_skb(dev, skb); |
150 | if (FREE_AFTER_TX(skb)) | ||
151 | p54_free_skb(dev, skb); | ||
152 | } | 149 | } |
153 | 150 | ||
154 | static void p54u_tx_dummy_cb(struct urb *urb) { } | 151 | static void p54u_tx_dummy_cb(struct urb *urb) { } |
@@ -230,7 +227,8 @@ static void p54u_tx_3887(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
230 | p54u_tx_dummy_cb, dev); | 227 | p54u_tx_dummy_cb, dev); |
231 | usb_fill_bulk_urb(data_urb, priv->udev, | 228 | usb_fill_bulk_urb(data_urb, priv->udev, |
232 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | 229 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), |
233 | skb->data, skb->len, p54u_tx_cb, skb); | 230 | skb->data, skb->len, FREE_AFTER_TX(skb) ? |
231 | p54u_tx_cb : p54u_tx_dummy_cb, skb); | ||
234 | 232 | ||
235 | usb_anchor_urb(addr_urb, &priv->submitted); | 233 | usb_anchor_urb(addr_urb, &priv->submitted); |
236 | err = usb_submit_urb(addr_urb, GFP_ATOMIC); | 234 | err = usb_submit_urb(addr_urb, GFP_ATOMIC); |
@@ -269,28 +267,24 @@ static void p54u_tx_lm87(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
269 | { | 267 | { |
270 | struct p54u_priv *priv = dev->priv; | 268 | struct p54u_priv *priv = dev->priv; |
271 | struct urb *data_urb; | 269 | struct urb *data_urb; |
272 | struct lm87_tx_hdr *hdr; | 270 | struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); |
273 | __le32 checksum; | ||
274 | __le32 addr = ((struct p54_hdr *)skb->data)->req_id; | ||
275 | 271 | ||
276 | data_urb = usb_alloc_urb(0, GFP_ATOMIC); | 272 | data_urb = usb_alloc_urb(0, GFP_ATOMIC); |
277 | if (!data_urb) | 273 | if (!data_urb) |
278 | return; | 274 | return; |
279 | 275 | ||
280 | checksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); | 276 | hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); |
281 | hdr = (struct lm87_tx_hdr *)skb_push(skb, sizeof(*hdr)); | 277 | hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; |
282 | hdr->chksum = checksum; | ||
283 | hdr->device_addr = addr; | ||
284 | 278 | ||
285 | usb_fill_bulk_urb(data_urb, priv->udev, | 279 | usb_fill_bulk_urb(data_urb, priv->udev, |
286 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | 280 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), |
287 | skb->data, skb->len, p54u_tx_cb, skb); | 281 | hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? |
282 | p54u_tx_cb : p54u_tx_dummy_cb, skb); | ||
288 | data_urb->transfer_flags |= URB_ZERO_PACKET; | 283 | data_urb->transfer_flags |= URB_ZERO_PACKET; |
289 | 284 | ||
290 | usb_anchor_urb(data_urb, &priv->submitted); | 285 | usb_anchor_urb(data_urb, &priv->submitted); |
291 | if (usb_submit_urb(data_urb, GFP_ATOMIC)) { | 286 | if (usb_submit_urb(data_urb, GFP_ATOMIC)) { |
292 | usb_unanchor_urb(data_urb); | 287 | usb_unanchor_urb(data_urb); |
293 | skb_pull(skb, sizeof(*hdr)); | ||
294 | p54_free_skb(dev, skb); | 288 | p54_free_skb(dev, skb); |
295 | } | 289 | } |
296 | usb_free_urb(data_urb); | 290 | usb_free_urb(data_urb); |
@@ -300,11 +294,9 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
300 | { | 294 | { |
301 | struct p54u_priv *priv = dev->priv; | 295 | struct p54u_priv *priv = dev->priv; |
302 | struct urb *int_urb, *data_urb; | 296 | struct urb *int_urb, *data_urb; |
303 | struct net2280_tx_hdr *hdr; | 297 | struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); |
304 | struct net2280_reg_write *reg; | 298 | struct net2280_reg_write *reg; |
305 | int err = 0; | 299 | int err = 0; |
306 | __le32 addr = ((struct p54_hdr *) skb->data)->req_id; | ||
307 | __le16 len = cpu_to_le16(skb->len); | ||
308 | 300 | ||
309 | reg = kmalloc(sizeof(*reg), GFP_ATOMIC); | 301 | reg = kmalloc(sizeof(*reg), GFP_ATOMIC); |
310 | if (!reg) | 302 | if (!reg) |
@@ -327,10 +319,9 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
327 | reg->addr = cpu_to_le32(P54U_DEV_BASE); | 319 | reg->addr = cpu_to_le32(P54U_DEV_BASE); |
328 | reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); | 320 | reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); |
329 | 321 | ||
330 | hdr = (void *)skb_push(skb, sizeof(*hdr)); | ||
331 | memset(hdr, 0, sizeof(*hdr)); | 322 | memset(hdr, 0, sizeof(*hdr)); |
332 | hdr->len = len; | 323 | hdr->len = cpu_to_le16(skb->len); |
333 | hdr->device_addr = addr; | 324 | hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; |
334 | 325 | ||
335 | usb_fill_bulk_urb(int_urb, priv->udev, | 326 | usb_fill_bulk_urb(int_urb, priv->udev, |
336 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), | 327 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), |
@@ -345,7 +336,8 @@ static void p54u_tx_net2280(struct ieee80211_hw *dev, struct sk_buff *skb) | |||
345 | 336 | ||
346 | usb_fill_bulk_urb(data_urb, priv->udev, | 337 | usb_fill_bulk_urb(data_urb, priv->udev, |
347 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), | 338 | usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), |
348 | skb->data, skb->len, p54u_tx_cb, skb); | 339 | hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? |
340 | p54u_tx_cb : p54u_tx_dummy_cb, skb); | ||
349 | 341 | ||
350 | usb_anchor_urb(int_urb, &priv->submitted); | 342 | usb_anchor_urb(int_urb, &priv->submitted); |
351 | err = usb_submit_urb(int_urb, GFP_ATOMIC); | 343 | err = usb_submit_urb(int_urb, GFP_ATOMIC); |