usbnet: don't clear urb->dev in tx_complete
[firefly-linux-kernel-4.4.55.git] / drivers / net / usb / usbnet.c
index 5394b4eb00b017052eb4130dd6e29837c9b63f0f..62f8b5cfbb56c3057f49309d2ad4fbd3d611a309 100644 (file)
@@ -328,13 +328,13 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
        unsigned long           lockflags;
        size_t                  size = dev->rx_urb_size;
 
-       if ((skb = alloc_skb (size + NET_IP_ALIGN, flags)) == NULL) {
+       skb = __netdev_alloc_skb_ip_align(dev->net, size, flags);
+       if (!skb) {
                netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
                usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
                usb_free_urb (urb);
                return -ENOMEM;
        }
-       skb_reserve (skb, NET_IP_ALIGN);
 
        entry = (struct skb_data *) skb->cb;
        entry->urb = urb;
@@ -589,6 +589,14 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
                entry = (struct skb_data *) skb->cb;
                urb = entry->urb;
 
+               /*
+                * Get reference count of the URB to avoid it to be
+                * freed during usb_unlink_urb, which may trigger
+                * use-after-free problem inside usb_unlink_urb since
+                * usb_unlink_urb is always racing with .complete
+                * handler(include defer_bh).
+                */
+               usb_get_urb(urb);
                spin_unlock_irqrestore(&q->lock, flags);
                // during some PM-driven resume scenarios,
                // these (async) unlinks complete immediately
@@ -597,6 +605,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
                        netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
                else
                        count++;
+               usb_put_urb(urb);
                spin_lock_irqsave(&q->lock, flags);
        }
        spin_unlock_irqrestore (&q->lock, flags);
@@ -1028,7 +1037,6 @@ static void tx_complete (struct urb *urb)
        }
 
        usb_autopm_put_interface_async(dev->intf);
-       urb->dev = NULL;
        entry->state = tx_done;
        defer_bh(dev, skb, &dev->txq);
 }