linux/drivers/net/usb
Eugene Shatokhin fcb0bb6aab usbnet: Fix a race between usbnet_stop() and the BH
The race may happen when a device (e.g. YOTA 4G LTE Modem) is
unplugged while the system is downloading a large file from the Net.

Hardware breakpoints and Kprobes with delays were used to confirm that
the race does actually happen.

The race is on skb_queue ('next' pointer) between usbnet_stop()
and rx_complete(), which, in turn, calls usbnet_bh().

Here is a part of the call stack with the code where the changes to the
queue happen. The line numbers are for the kernel 4.1.0:

*0 __skb_unlink (skbuff.h:1517)
    prev->next = next;
*1 defer_bh (usbnet.c:430)
    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);
    __skb_queue_tail(&dev->done, skb);
    if (dev->done.qlen == 1)
        tasklet_schedule(&dev->bh);
    spin_unlock_irqrestore(&dev->done.lock, flags);
*2 rx_complete (usbnet.c:640)
    state = defer_bh(dev, skb, &dev->rxq, state);

At the same time, the following code repeatedly checks if the queue is
empty and reads these values concurrently with the above changes:

*0  usbnet_terminate_urbs (usbnet.c:765)
    /* maybe wait for deletions to finish. */
    while (!skb_queue_empty(&dev->rxq)
        && !skb_queue_empty(&dev->txq)
        && !skb_queue_empty(&dev->done)) {
            schedule_timeout(msecs_to_jiffies(UNLINK_TIMEOUT_MS));
            set_current_state(TASK_UNINTERRUPTIBLE);
            netif_dbg(dev, ifdown, dev->net,
                  "waited for %d urb completions\n", temp);
    }
*1  usbnet_stop (usbnet.c:806)
    if (!(info->flags & FLAG_AVOID_UNLINK_URBS))
        usbnet_terminate_urbs(dev);

As a result, it is possible, for example, that the skb is removed from
dev->rxq by __skb_unlink() before the check
"!skb_queue_empty(&dev->rxq)" in usbnet_terminate_urbs() is made. It is
also possible in this case that the skb is added to dev->done queue
after "!skb_queue_empty(&dev->done)" is checked. So
usbnet_terminate_urbs() may stop waiting and return while dev->done
queue still has an item.

Locking in defer_bh() and usbnet_terminate_urbs() was revisited to avoid
this race.

Signed-off-by: Eugene Shatokhin <eugene.shatokhin@rosalab.ru>
Reviewed-by: Bjørn Mork <bjorn@mork.no>
Acked-by: Oliver Neukum <oneukum@suse.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-09-08 13:17:43 -07:00
..
asix_common.c usbnet: Fix tx_bytes statistic running backward in cdc_ncm 2015-03-29 12:06:45 -07:00
asix_devices.c net: asix: add support for the Sitecom LN-028 USB adapter 2015-02-27 17:12:20 -05:00
asix.h
ax88172a.c
ax88179_178a.c ax88179_178a: fix bonding failure 2014-10-20 00:53:30 -04:00
catc.c net: usb: Use eth_<foo>_addr instead of memset 2015-03-03 17:01:36 -05:00
cdc_eem.c
cdc_ether.c drivers/net/usb: add device id for NVIDIA Tegra USB 3.0 Ethernet 2015-07-08 23:58:20 -07:00
cdc_mbim.c cdc_ncm: Add support for moving NDP to end of NCM frame 2015-07-09 14:58:31 -07:00
cdc_ncm.c cdc_ncm: update specs URL 2015-07-11 21:12:23 -07:00
cdc_subset.c cdc_subset: deal with a device that needs reset for timeout 2014-08-02 15:44:18 -07:00
cdc-phonet.c phonet: Replace calls to __skb_alloc_page with __dev_alloc_page 2014-11-12 00:00:14 -05:00
cx82310_eth.c cx82310_eth: fix semicolon.cocci warnings 2015-03-24 14:56:02 -04:00
dm9601.c
gl620a.c
hso.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2015-03-03 21:16:48 -05:00
huawei_cdc_ncm.c cdc_ncm: Add support for moving NDP to end of NCM frame 2015-07-09 14:58:31 -07:00
int51x1.c
ipheth.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2014-06-03 23:32:12 -07:00
kalmia.c
kaweth.c usb/kaweth: use GFP_ATOMIC under spin_lock in usb_start_wait_urb() 2015-01-12 16:42:49 -05:00
Kconfig Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver 2015-07-31 15:19:00 -07:00
lan78xx.c lan78xx: Fix ladv/radv error handling in lan78xx_link_reset() 2015-09-06 19:46:56 -07:00
lan78xx.h Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver 2015-07-31 15:19:00 -07:00
lg-vl600.c net: usb: Use eth_<foo>_addr instead of memset 2015-03-03 17:01:36 -05:00
Makefile Microchip's LAN7800 family USB 2/3 to 10/100/1000 Ethernet device driver 2015-07-31 15:19:00 -07:00
mcs7830.c
net1080.c
pegasus.c
pegasus.h
plusb.c usb: plusb: Add support for National Instruments host-to-host cable 2015-02-20 15:12:48 -05:00
qmi_wwan.c net: qmi_wwan: Sierra Wireless MC73xx -> Sierra Wireless MC7304/MC7354 2015-08-31 15:16:17 -07:00
r8152.c Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net 2015-07-31 23:52:20 -07:00
rndis_host.c
rtl8150.c usbnet: rtl8150: remove unused variable 2014-11-19 15:17:11 -05:00
sierra_net.c
smsc75xx.c
smsc75xx.h
smsc95xx.c usbnet: smsc95xx: dereferencing NULL pointer 2014-11-11 16:24:08 -05:00
smsc95xx.h
sr9700.c net: usb: sr9700: Use 'SR_' prefix for the common register macros 2015-02-04 13:53:02 -08:00
sr9700.h net: usb: sr9700: Use 'SR_' prefix for the common register macros 2015-02-04 13:53:02 -08:00
sr9800.c usbnet: Fix tx_bytes statistic running backward in cdc_ncm 2015-03-29 12:06:45 -07:00
sr9800.h
usbnet.c usbnet: Fix a race between usbnet_stop() and the BH 2015-09-08 13:17:43 -07:00
zaurus.c