diff options
| author | Oliver Neukum <oneukum@suse.de> | 2014-03-26 14:32:51 +0100 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-03-27 14:59:10 -0400 | 
| commit | 14a0d635d18d0fb552dcc979d6d25106e6541f2e (patch) | |
| tree | 2fa1bce86903a0f841ab662eb85de4445bbfe398 /drivers/net/usb | |
| parent | 681daee2443291419c57cccb0671f5f94a839005 (diff) | |
usbnet: include wait queue head in device structure
This fixes a race which happens by freeing an object on the stack.
Quoting Julius:
> The issue is
> that it calls usbnet_terminate_urbs() before that, which temporarily
> installs a waitqueue in dev->wait in order to be able to wait on the
> tasklet to run and finish up some queues. The waiting itself looks
> okay, but the access to 'dev->wait' is totally unprotected and can
> race arbitrarily. I think in this case usbnet_bh() managed to succeed
> it's dev->wait check just before usbnet_terminate_urbs() sets it back
> to NULL. The latter then finishes and the waitqueue_t structure on its
> stack gets overwritten by other functions halfway through the
> wake_up() call in usbnet_bh().
The fix is to just not allocate the data structure on the stack.
As dev->wait is abused as a flag it also takes a runtime PM change
to fix this bug.
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Reported-by: Grant Grundler <grundler@google.com>
Tested-by: Grant Grundler <grundler@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
| -rw-r--r-- | drivers/net/usb/usbnet.c | 33 | 
1 files changed, 19 insertions, 14 deletions
| diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index dd10d5817d2a..f9e96c427558 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -752,14 +752,12 @@ EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);  // precondition: never called in_interrupt  static void usbnet_terminate_urbs(struct usbnet *dev)  { -	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(unlink_wakeup);  	DECLARE_WAITQUEUE(wait, current);  	int temp;  	/* ensure there are no more active urbs */ -	add_wait_queue(&unlink_wakeup, &wait); +	add_wait_queue(&dev->wait, &wait);  	set_current_state(TASK_UNINTERRUPTIBLE); -	dev->wait = &unlink_wakeup;  	temp = unlink_urbs(dev, &dev->txq) +  		unlink_urbs(dev, &dev->rxq); @@ -773,15 +771,14 @@ static void usbnet_terminate_urbs(struct usbnet *dev)  				  "waited for %d urb completions\n", temp);  	}  	set_current_state(TASK_RUNNING); -	dev->wait = NULL; -	remove_wait_queue(&unlink_wakeup, &wait); +	remove_wait_queue(&dev->wait, &wait);  }  int usbnet_stop (struct net_device *net)  {  	struct usbnet		*dev = netdev_priv(net);  	struct driver_info	*info = dev->driver_info; -	int			retval; +	int			retval, pm;  	clear_bit(EVENT_DEV_OPEN, &dev->flags);  	netif_stop_queue (net); @@ -791,6 +788,8 @@ int usbnet_stop (struct net_device *net)  		   net->stats.rx_packets, net->stats.tx_packets,  		   net->stats.rx_errors, net->stats.tx_errors); +	/* to not race resume */ +	pm = usb_autopm_get_interface(dev->intf);  	/* allow minidriver to stop correctly (wireless devices to turn off  	 * radio etc) */  	if (info->stop) { @@ -817,6 +816,9 @@ int usbnet_stop (struct net_device *net)  	dev->flags = 0;  	del_timer_sync (&dev->delay);  	tasklet_kill (&dev->bh); +	if (!pm) +		usb_autopm_put_interface(dev->intf); +  	if (info->manage_power &&  	    !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags))  		info->manage_power(dev, 0); @@ -1437,11 +1439,12 @@ static void usbnet_bh (unsigned long param)  	/* restart RX again after disabling due to high error rate */  	clear_bit(EVENT_RX_KILL, &dev->flags); -	// waiting for all pending urbs to complete? -	if (dev->wait) { -		if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { -			wake_up (dev->wait); -		} +	/* waiting for all pending urbs to complete? +	 * only then can we forgo submitting anew +	 */ +	if (waitqueue_active(&dev->wait)) { +		if (dev->txq.qlen + dev->rxq.qlen + dev->done.qlen == 0) +			wake_up_all(&dev->wait);  	// or are we maybe short a few urbs?  	} else if (netif_running (dev->net) && @@ -1580,6 +1583,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)  	dev->driver_name = name;  	dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV  				| NETIF_MSG_PROBE | NETIF_MSG_LINK); +	init_waitqueue_head(&dev->wait);  	skb_queue_head_init (&dev->rxq);  	skb_queue_head_init (&dev->txq);  	skb_queue_head_init (&dev->done); @@ -1791,9 +1795,10 @@ int usbnet_resume (struct usb_interface *intf)  		spin_unlock_irq(&dev->txq.lock);  		if (test_bit(EVENT_DEV_OPEN, &dev->flags)) { -			/* handle remote wakeup ASAP */ -			if (!dev->wait && -				netif_device_present(dev->net) && +			/* handle remote wakeup ASAP +			 * we cannot race against stop +			 */ +			if (netif_device_present(dev->net) &&  				!timer_pending(&dev->delay) &&  				!test_bit(EVENT_RX_HALT, &dev->flags))  					rx_alloc_submit(dev, GFP_NOIO); | 
