Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Christian Lamparter and committed by
John W. Linville
e2fe154e 12da401e

+13 -21
+13 -21
drivers/net/wireless/p54/p54usb.c
··· 144 144 struct sk_buff *skb = urb->context; 145 145 struct ieee80211_hw *dev = (struct ieee80211_hw *) 146 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); 150 - if (FREE_AFTER_TX(skb)) 151 - p54_free_skb(dev, skb); 148 + p54_free_skb(dev, skb); 152 149 } 153 150 154 151 static void p54u_tx_dummy_cb(struct urb *urb) { } ··· 227 230 p54u_tx_dummy_cb, dev); 228 231 usb_fill_bulk_urb(data_urb, priv->udev, 229 232 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), 230 - skb->data, skb->len, p54u_tx_cb, skb); 233 + skb->data, skb->len, FREE_AFTER_TX(skb) ? 234 + p54u_tx_cb : p54u_tx_dummy_cb, skb); 231 235 232 236 usb_anchor_urb(addr_urb, &priv->submitted); 233 237 err = usb_submit_urb(addr_urb, GFP_ATOMIC); ··· 267 269 { 268 270 struct p54u_priv *priv = dev->priv; 269 271 struct urb *data_urb; 270 - struct lm87_tx_hdr *hdr; 271 - __le32 checksum; 272 - __le32 addr = ((struct p54_hdr *)skb->data)->req_id; 272 + struct lm87_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); 273 273 274 274 data_urb = usb_alloc_urb(0, GFP_ATOMIC); 275 275 if (!data_urb) 276 276 return; 277 277 278 - checksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); 279 - hdr = (struct lm87_tx_hdr *)skb_push(skb, sizeof(*hdr)); 280 - hdr->chksum = checksum; 281 - hdr->device_addr = addr; 278 + hdr->chksum = p54u_lm87_chksum((__le32 *)skb->data, skb->len); 279 + hdr->device_addr = ((struct p54_hdr *)skb->data)->req_id; 282 280 283 281 usb_fill_bulk_urb(data_urb, priv->udev, 284 282 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), 285 - skb->data, skb->len, p54u_tx_cb, skb); 283 + hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? 284 + p54u_tx_cb : p54u_tx_dummy_cb, skb); 286 285 data_urb->transfer_flags |= URB_ZERO_PACKET; 287 286 288 287 usb_anchor_urb(data_urb, &priv->submitted); 289 288 if (usb_submit_urb(data_urb, GFP_ATOMIC)) { 290 289 usb_unanchor_urb(data_urb); 291 - skb_pull(skb, sizeof(*hdr)); 292 290 p54_free_skb(dev, skb); 293 291 } 294 292 usb_free_urb(data_urb); ··· 294 300 { 295 301 struct p54u_priv *priv = dev->priv; 296 302 struct urb *int_urb, *data_urb; 297 - struct net2280_tx_hdr *hdr; 303 + struct net2280_tx_hdr *hdr = (void *)skb->data - sizeof(*hdr); 298 304 struct net2280_reg_write *reg; 299 305 int err = 0; 300 - __le32 addr = ((struct p54_hdr *) skb->data)->req_id; 301 - __le16 len = cpu_to_le16(skb->len); 302 306 303 307 reg = kmalloc(sizeof(*reg), GFP_ATOMIC); 304 308 if (!reg) ··· 319 327 reg->addr = cpu_to_le32(P54U_DEV_BASE); 320 328 reg->val = cpu_to_le32(ISL38XX_DEV_INT_DATA); 321 329 322 - hdr = (void *)skb_push(skb, sizeof(*hdr)); 323 330 memset(hdr, 0, sizeof(*hdr)); 324 - hdr->len = len; 325 - hdr->device_addr = addr; 331 + hdr->len = cpu_to_le16(skb->len); 332 + hdr->device_addr = ((struct p54_hdr *) skb->data)->req_id; 326 333 327 334 usb_fill_bulk_urb(int_urb, priv->udev, 328 335 usb_sndbulkpipe(priv->udev, P54U_PIPE_DEV), reg, sizeof(*reg), ··· 336 345 337 346 usb_fill_bulk_urb(data_urb, priv->udev, 338 347 usb_sndbulkpipe(priv->udev, P54U_PIPE_DATA), 339 - skb->data, skb->len, p54u_tx_cb, skb); 348 + hdr, skb->len + sizeof(*hdr), FREE_AFTER_TX(skb) ? 349 + p54u_tx_cb : p54u_tx_dummy_cb, skb); 340 350 341 351 usb_anchor_urb(int_urb, &priv->submitted); 342 352 err = usb_submit_urb(int_urb, GFP_ATOMIC);