usbnet: fix skb traversing races during unlink(v2)
[linux-flexiantxendom0-3.2.10.git] / drivers / net / usb / usbnet.c
index ce395fe..b38db48 100644 (file)
@@ -210,6 +210,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)
                } else {
                        usb_fill_int_urb(dev->interrupt, dev->udev, pipe,
                                buf, maxp, intr_complete, dev, period);
+                       dev->interrupt->transfer_flags |= URB_FREE_BUFFER;
                        dev_dbg(&intf->dev,
                                "status ep%din, %d bytes period %d\n",
                                usb_pipeendpoint(pipe), maxp, period);
@@ -238,6 +239,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
        netif_dbg(dev, rx_status, dev->net, "< rx, len %zu, type 0x%x\n",
                  skb->len + sizeof (struct ethhdr), skb->protocol);
        memset (skb->cb, 0, sizeof (struct skb_data));
+
+       if (skb_defer_rx_timestamp(skb))
+               return;
+
        status = netif_rx (skb);
        if (status != NET_RX_SUCCESS)
                netif_dbg(dev, rx_err, dev->net,
@@ -277,17 +282,32 @@ int usbnet_change_mtu (struct net_device *net, int new_mtu)
 }
 EXPORT_SYMBOL_GPL(usbnet_change_mtu);
 
+/* The caller must hold list->lock */
+static void __usbnet_queue_skb(struct sk_buff_head *list,
+                       struct sk_buff *newsk, enum skb_state state)
+{
+       struct skb_data *entry = (struct skb_data *) newsk->cb;
+
+       __skb_queue_tail(list, newsk);
+       entry->state = state;
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* some LK 2.4 HCDs oopsed if we freed or resubmitted urbs from
  * completion callbacks.  2.5 should have fixed those bugs...
  */
 
-static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_head *list)
+static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
+               struct sk_buff_head *list, enum skb_state state)
 {
        unsigned long           flags;
+       enum skb_state          old_state;
+       struct skb_data *entry = (struct skb_data *) skb->cb;
 
        spin_lock_irqsave(&list->lock, flags);
+       old_state = entry->state;
+       entry->state = state;
        __skb_unlink(skb, list);
        spin_unlock(&list->lock);
        spin_lock(&dev->done.lock);
@@ -295,6 +315,7 @@ static void defer_bh(struct usbnet *dev, struct sk_buff *skb, struct sk_buff_hea
        if (dev->done.qlen == 1)
                tasklet_schedule(&dev->bh);
        spin_unlock_irqrestore(&dev->done.lock, flags);
+       return old_state;
 }
 
 /* some work can't be done in tasklets, so we use keventd
@@ -324,18 +345,17 @@ 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;
        entry->dev = dev;
-       entry->state = rx_start;
        entry->length = 0;
 
        usb_fill_bulk_urb (urb, dev->udev, dev->in,
@@ -367,7 +387,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
                        tasklet_schedule (&dev->bh);
                        break;
                case 0:
-                       __skb_queue_tail (&dev->rxq, skb);
+                       __usbnet_queue_skb(&dev->rxq, skb, rx_start);
                }
        } else {
                netif_dbg(dev, ifdown, dev->net, "rx: stopped\n");
@@ -418,16 +438,17 @@ static void rx_complete (struct urb *urb)
        struct skb_data         *entry = (struct skb_data *) skb->cb;
        struct usbnet           *dev = entry->dev;
        int                     urb_status = urb->status;
+       enum skb_state          state;
 
        skb_put (skb, urb->actual_length);
-       entry->state = rx_done;
+       state = rx_done;
        entry->urb = NULL;
 
        switch (urb_status) {
        /* success */
        case 0:
                if (skb->len < dev->net->hard_header_len) {
-                       entry->state = rx_cleanup;
+                       state = rx_cleanup;
                        dev->net->stats.rx_errors++;
                        dev->net->stats.rx_length_errors++;
                        netif_dbg(dev, rx_err, dev->net,
@@ -466,7 +487,7 @@ static void rx_complete (struct urb *urb)
                                  "rx throttle %d\n", urb_status);
                }
 block:
-               entry->state = rx_cleanup;
+               state = rx_cleanup;
                entry->urb = urb;
                urb = NULL;
                break;
@@ -477,18 +498,20 @@ block:
                // FALLTHROUGH
 
        default:
-               entry->state = rx_cleanup;
+               state = rx_cleanup;
                dev->net->stats.rx_errors++;
                netif_dbg(dev, rx_err, dev->net, "rx status %d\n", urb_status);
                break;
        }
 
-       defer_bh(dev, skb, &dev->rxq);
+       state = defer_bh(dev, skb, &dev->rxq, state);
 
        if (urb) {
                if (netif_running (dev->net) &&
-                   !test_bit (EVENT_RX_HALT, &dev->flags)) {
+                   !test_bit (EVENT_RX_HALT, &dev->flags) &&
+                   state != unlink_start) {
                        rx_submit (dev, urb, GFP_ATOMIC);
+                       usb_mark_last_busy(dev->udev);
                        return;
                }
                usb_free_urb (urb);
@@ -573,18 +596,34 @@ EXPORT_SYMBOL_GPL(usbnet_purge_paused_rxq);
 static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
 {
        unsigned long           flags;
-       struct sk_buff          *skb, *skbnext;
+       struct sk_buff          *skb;
        int                     count = 0;
 
        spin_lock_irqsave (&q->lock, flags);
-       skb_queue_walk_safe(q, skb, skbnext) {
+       while (!skb_queue_empty(q)) {
                struct skb_data         *entry;
                struct urb              *urb;
                int                     retval;
 
-               entry = (struct skb_data *) skb->cb;
+               skb_queue_walk(q, skb) {
+                       entry = (struct skb_data *) skb->cb;
+                       if (entry->state != unlink_start)
+                               goto found;
+               }
+               break;
+found:
+               entry->state = unlink_start;
                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
                retval = usb_unlink_urb (urb);
@@ -592,6 +631,8 @@ 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);
        return count;
@@ -1022,9 +1063,7 @@ 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);
+       (void) defer_bh(dev, skb, &dev->txq, tx_done);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -1053,6 +1092,9 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
        unsigned long           flags;
        int retval;
 
+       if (skb)
+               skb_tx_timestamp(skb);
+
        // some devices want funky USB-level framing, for
        // win32 driver (usually) and/or hardware quirks
        if (info->tx_fixup) {
@@ -1077,7 +1119,6 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
        entry = (struct skb_data *) skb->cb;
        entry->urb = urb;
        entry->dev = dev;
-       entry->state = tx_start;
        entry->length = length;
 
        usb_fill_bulk_urb (urb, dev->udev, dev->out,
@@ -1136,7 +1177,7 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb,
                break;
        case 0:
                net->trans_start = jiffies;
-               __skb_queue_tail (&dev->txq, skb);
+               __usbnet_queue_skb(&dev->txq, skb, tx_start);
                if (dev->txq.qlen >= TX_QLEN (dev))
                        netif_stop_queue (net);
        }
@@ -1327,10 +1368,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 
        // set up our own records
        net = alloc_etherdev(sizeof(*dev));
-       if (!net) {
-               dbg ("can't kmalloc dev");
+       if (!net)
                goto out;
-       }
 
        /* netdev_printk() needs this so do it as early as possible */
        SET_NETDEV_DEV(net, &udev->dev);
@@ -1427,7 +1466,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 
        status = register_netdev (net);
        if (status)
-               goto out3;
+               goto out4;
        netif_info(dev, probe, dev->net,
                   "register '%s' at usb-%s-%s, %s, %pM\n",
                   udev->dev.driver->name,
@@ -1445,6 +1484,8 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 
        return 0;
 
+out4:
+       usb_free_urb(dev->interrupt);
 out3:
        if (info->unbind)
                info->unbind (dev, udev);
@@ -1470,7 +1511,7 @@ int usbnet_suspend (struct usb_interface *intf, pm_message_t message)
        if (!dev->suspend_count++) {
                spin_lock_irq(&dev->txq.lock);
                /* don't autosuspend while transmitting */
-               if (dev->txq.qlen && (message.event & PM_EVENT_AUTO)) {
+               if (dev->txq.qlen && PMSG_IS_AUTO(message)) {
                        spin_unlock_irq(&dev->txq.lock);
                        return -EBUSY;
                } else {
@@ -1528,7 +1569,7 @@ int usbnet_resume (struct usb_interface *intf)
 
                if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
                        if (!(dev->txq.qlen >= TX_QLEN(dev)))
-                               netif_start_queue(dev->net);
+                               netif_tx_wake_all_queues(dev->net);
                        tasklet_schedule (&dev->bh);
                }
        }