CVE-2011-4112: Insufficient Information (NVD-CWE-noinfo)
value: The net subsystem in the Linux kernel before 3.1 does not properly restrict use of the IFF_TX_SKB_SHARING flag, which allows local users to cause a denial of service (panic) by leveraging the CAP_NET_ADMIN capability to access /proc/net/pktgen/pgctrl, and then using the pktgen package in conjunction with a bridge device for a VLAN interface.
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
CVSS v3 base score: 5.5
Published: 2012-05-17T11:00Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: drivers/isdn/i4l/isdn_net.c, drivers/net/bonding/bond_main.c, drivers/net/ifb.c, drivers/net/macvlan.c, drivers/net/tun.c, drivers/net/veth.c, drivers/net/wan/hdlc_fr.c, drivers/net/wireless/airo.c, drivers/net/wireless/hostap/hostap_main.c, drivers/staging/ath6kl/os/linux/ar6000_drv.c, net/8021q/vlan_dev.c, net/bluetooth/bnep/netdev.c, net/l2tp/l2tp_eth.c, net/mac80211/iface.c
Commit date: 2011-07-27 22:39:30 -0700
Commit message: net: Audit drivers to identify those needing IFF_TX_SKB_SHARING cleared

After the last patch, We are left in a state in which only drivers calling
ether_setup have IFF_TX_SKB_SHARING set (we assume that drivers touching real
hardware call ether_setup for their net_devices and don't hold any state in
their skbs.  There are a handful of drivers that violate this assumption of
course, and need to be fixed up.  This patch identifies those drivers, and marks
them as not being able to support the safe transmission of skbs by clearning the
IFF_TX_SKB_SHARING flag in priv_flags

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Karsten Keil <isdn@linux-pingi.de>
CC: "David S. Miller" <davem@davemloft.net>
CC: Jay Vosburgh <fubar@us.ibm.com>
CC: Andy Gospodarek <andy@greyhouse.net>
CC: Patrick McHardy <kaber@trash.net>
CC: Krzysztof Halasa <khc@pm.waw.pl>
CC: "John W. Linville" <linville@tuxdriver.com>
CC: Greg Kroah-Hartman <gregkh@suse.de>
CC: Marcel Holtmann <marcel@holtmann.org>
CC: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

Diff: diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 48e9cc0369b10..1f73d7f7e0242 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -2530,10 +2530,13 @@ static void _isdn_setup(struct net_device *dev)
 
 	ether_setup(dev);
 
 	/* Setup the generic properties */
 	dev->flags = IFF_NOARP|IFF_POINTOPOINT;
+
+	/* isdn prepends a header in the tx path, can't share skbs */
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->header_ops = NULL;
 	dev->netdev_ops = &isdn_netdev_ops;
 
 	/* for clients with MPPP maybe higher values better */
 	dev->tx_queue_len = 30;
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 02842d05c11f4..df21e84f8198f 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1555,12 +1555,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
 			dev_uc_flush(bond_dev);
 			dev_mc_flush(bond_dev);
 
 			if (slave_dev->type != ARPHRD_ETHER)
 				bond_setup_by_slave(bond_dev, slave_dev);
-			else
+			else {
 				ether_setup(bond_dev);
+				bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+			}
 
 			netdev_bonding_change(bond_dev,
 					      NETDEV_POST_TYPE_CHANGE);
 		}
 	} else if (bond_dev->type != slave_dev->type) {
@@ -4328,11 +4330,11 @@ static void bond_setup(struct net_device *bond_dev)
 
 	/* Initialize the device options */
 	bond_dev->tx_queue_len = 0;
 	bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
 	bond_dev->priv_flags |= IFF_BONDING;
-	bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+	bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 
 	/* At first, we block adding VLANs. That's the only way to
 	 * prevent problems that occur when adding VLANs over an
 	 * empty bond. The block will be removed once non-challenged
 	 * slaves are enslaved.
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 6e82dd32e8063..46b5f5fd686ba 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -181,11 +181,11 @@ static void ifb_setup(struct net_device *dev)
 	dev->features |= IFB_FEATURES;
 	dev->vlan_features |= IFB_FEATURES;
 
 	dev->flags |= IFF_NOARP;
 	dev->flags &= ~IFF_MULTICAST;
-	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
+	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 	random_ether_addr(dev->dev_addr);
 }
 
 static netdev_tx_t ifb_xmit(struct sk_buff *skb, struct net_device *dev)
 {
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index ba631fcece34d..05172c39a0cea 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -570,11 +570,11 @@ static const struct net_device_ops macvlan_netdev_ops = {
 
 void macvlan_common_setup(struct net_device *dev)
 {
 	ether_setup(dev);
 
-	dev->priv_flags	       &= ~IFF_XMIT_DST_RELEASE;
+	dev->priv_flags	       &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 	dev->netdev_ops		= &macvlan_netdev_ops;
 	dev->destructor		= free_netdev;
 	dev->header_ops		= &macvlan_hard_header_ops,
 	dev->ethtool_ops	= &macvlan_ethtool_ops;
 }
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 9a6b3824da146..71f3d1a35b74e 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -526,10 +526,11 @@ static void tun_net_init(struct net_device *dev)
 
 	case TUN_TAP_DEV:
 		dev->netdev_ops = &tap_netdev_ops;
 		/* Ethernet TAP Device */
 		ether_setup(dev);
+		dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
 		random_ether_addr(dev->dev_addr);
 
 		dev->tx_queue_len = TUN_READQ_SIZE;  /* We prefer our own queue length */
 		break;
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 7f78db7bd68db..5b23767ea8172 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -261,10 +261,12 @@ static const struct net_device_ops veth_netdev_ops = {
 
 static void veth_setup(struct net_device *dev)
 {
 	ether_setup(dev);
 
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+
 	dev->netdev_ops = &veth_netdev_ops;
 	dev->ethtool_ops = &veth_ethtool_ops;
 	dev->features |= NETIF_F_LLTX;
 	dev->destructor = veth_dev_free;
 
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index b25c9229a6a94..eb2028187fbe1 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -1072,13 +1072,14 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
 	if (*get_dev_p(pvc, type))
 		return -EEXIST;
 
 	used = pvc_is_used(pvc);
 
-	if (type == ARPHRD_ETHER)
+	if (type == ARPHRD_ETHER) {
 		dev = alloc_netdev(0, "pvceth%d", ether_setup);
-	else
+		dev->priv_flags &= ~IFF_TX_SKB_SHARING;
+	} else
 		dev = alloc_netdev(0, "pvc%d", pvc_setup);
 
 	if (!dev) {
 		netdev_warn(frad, "Memory squeeze on fr_pvc()\n");
 		delete_unused_pvcs(hdlc);
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 55cf71fbffe32..e1b3e3c134fdf 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -2821,10 +2821,11 @@ static struct net_device *_init_airo_card( unsigned short irq, int port,
 	dev->wireless_handlers = &airo_handler_def;
 	ai->wireless_data.spy_data = &ai->spy_data;
 	dev->wireless_data = &ai->wireless_data;
 	dev->irq = irq;
 	dev->base_addr = port;
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
 	SET_NETDEV_DEV(dev, dmdev);
 
 	reset_card (dev, 1);
 	msleep(400);
diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c
index d5084829c9e54..89a116fba1de2 100644
--- a/drivers/net/wireless/hostap/hostap_main.c
+++ b/drivers/net/wireless/hostap/hostap_main.c
@@ -853,10 +853,11 @@ void hostap_setup_dev(struct net_device *dev, local_info_t *local,
 {
 	struct hostap_interface *iface;
 
 	iface = netdev_priv(dev);
 	ether_setup(dev);
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
 	/* kernel callbacks */
 	if (iface) {
 		/* Currently, we point to the proper spy_data only on
 		 * the main_dev. This could be fixed. Jean II */
diff --git a/drivers/staging/ath6kl/os/linux/ar6000_drv.c b/drivers/staging/ath6kl/os/linux/ar6000_drv.c
index 48dd9e3635967..8ff52899ddee1 100644
--- a/drivers/staging/ath6kl/os/linux/ar6000_drv.c
+++ b/drivers/staging/ath6kl/os/linux/ar6000_drv.c
@@ -6177,10 +6177,11 @@ int ar6000_create_ap_interface(struct ar6_softc *ar, char *ap_ifname)
         return A_ERROR;
     } 
     
     ether_setup(dev);
     init_netdev(dev, ap_ifname);
+    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 
     if (register_netdev(dev)) {
         AR_DEBUG_PRINTF(ATH_DEBUG_ERR,("ar6000_create_ap_interface: register_netdev failed\n"));
         return A_ERROR;
     }
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 934e221c1d071..9d40a071d0382 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -693,11 +693,11 @@ static const struct net_device_ops vlan_netdev_ops = {
 void vlan_setup(struct net_device *dev)
 {
 	ether_setup(dev);
 
 	dev->priv_flags		|= IFF_802_1Q_VLAN;
-	dev->priv_flags		&= ~IFF_XMIT_DST_RELEASE;
+	dev->priv_flags		&= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
 	dev->tx_queue_len	= 0;
 
 	dev->netdev_ops		= &vlan_netdev_ops;
 	dev->destructor		= free_netdev;
 	dev->ethtool_ops	= &vlan_ethtool_ops;
diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
index 8c100c9dae283..d4f5dff7c9551 100644
--- a/net/bluetooth/bnep/netdev.c
+++ b/net/bluetooth/bnep/netdev.c
@@ -229,9 +229,10 @@ void bnep_net_setup(struct net_device *dev)
 
 	memset(dev->broadcast, 0xff, ETH_ALEN);
 	dev->addr_len = ETH_ALEN;
 
 	ether_setup(dev);
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->netdev_ops = &bnep_netdev_ops;
 
 	dev->watchdog_timeo  = HZ * 2;
 }
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c
index a8193f52c13c2..d2726a74597d1 100644
--- a/net/l2tp/l2tp_eth.c
+++ b/net/l2tp/l2tp_eth.c
@@ -101,11 +101,11 @@ static struct net_device_ops l2tp_eth_netdev_ops = {
 };
 
 static void l2tp_eth_dev_setup(struct net_device *dev)
 {
 	ether_setup(dev);
-
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->netdev_ops		= &l2tp_eth_netdev_ops;
 	dev->destructor		= free_netdev;
 }
 
 static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len)
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index cd5fb40d3fd4b..556e7e6ddf0a6 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -696,10 +696,11 @@ static const struct net_device_ops ieee80211_monitorif_ops = {
 };
 
 static void ieee80211_if_setup(struct net_device *dev)
 {
 	ether_setup(dev);
+	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
 	dev->netdev_ops = &ieee80211_dataif_ops;
 	dev->destructor = free_netdev;
 }
 
 static void ieee80211_iface_work(struct work_struct *work)

Vulnerable code: 			else
	bond_dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
	dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
	dev->priv_flags	       &= ~IFF_XMIT_DST_RELEASE;
	if (type == ARPHRD_ETHER)
	else
	dev->priv_flags		&= ~IFF_XMIT_DST_RELEASE;

Fixed code: 
	/* isdn prepends a header in the tx path, can't share skbs */
	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
			else {
				bond_dev->priv_flags &= ~IFF_TX_SKB_SHARING;
			}
	bond_dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
	dev->priv_flags &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
	dev->priv_flags	       &= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
		dev->priv_flags &= ~IFF_TX_SKB_SHARING;
	dev->priv_flags &= ~IFF_TX_SKB_SHARING;

	if (type == ARPHRD_ETHER) {
		dev->priv_flags &= ~IFF_TX_SKB_SHARING;
	} else
	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
    dev->priv_flags &= ~IFF_TX_SKB_SHARING;
	dev->priv_flags		&= ~(IFF_XMIT_DST_RELEASE | IFF_TX_SKB_SHARING);
	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
	dev->priv_flags &= ~IFF_TX_SKB_SHARING;
	dev->priv_flags &= ~IFF_TX_SKB_SHARING;