Browse Source

Merge branch 'netdev-name'

Cong Wang says:

====================
net: forbid net devices named "all" "default" or "config"

/proc/sys/net/ipv[46]/conf/<dev> could conflict with
/proc/sys/net/ipv[46]/conf/(all|default). And /proc/net/vlan/<dev>
could conflict with /proc/net/vlan/config. Besides kernel warnings,
undefined behavior such as duplicated proc files also appears, therefore
we should forbid these names.

v2: introduce a helper function, suggested by Florian
    fix error handling for ipv6_add_dev() in addrconf_init()
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 11 years ago
parent
commit
772e7023e6
5 changed files with 102 additions and 45 deletions
  1. 6 0
      include/net/ip.h
  2. 13 8
      net/8021q/vlan.c
  3. 2 0
      net/8021q/vlanproc.c
  4. 27 9
      net/ipv4/devinet.c
  5. 54 28
      net/ipv6/addrconf.c

+ 6 - 0
include/net/ip.h

@@ -216,6 +216,12 @@ static inline int inet_is_local_reserved_port(struct net *net, int port)
 		return 0;
 		return 0;
 	return test_bit(port, net->ipv4.sysctl_local_reserved_ports);
 	return test_bit(port, net->ipv4.sysctl_local_reserved_ports);
 }
 }
+
+static inline bool sysctl_dev_name_is_allowed(const char *name)
+{
+	return strcmp(name, "default") != 0  && strcmp(name, "all") != 0;
+}
+
 #else
 #else
 static inline int inet_is_local_reserved_port(struct net *net, int port)
 static inline int inet_is_local_reserved_port(struct net *net, int port)
 {
 {

+ 13 - 8
net/8021q/vlan.c

@@ -325,23 +325,24 @@ static void vlan_transfer_features(struct net_device *dev,
 	netdev_update_features(vlandev);
 	netdev_update_features(vlandev);
 }
 }
 
 
-static void __vlan_device_event(struct net_device *dev, unsigned long event)
+static int __vlan_device_event(struct net_device *dev, unsigned long event)
 {
 {
+	int err = 0;
+
 	switch (event) {
 	switch (event) {
 	case NETDEV_CHANGENAME:
 	case NETDEV_CHANGENAME:
 		vlan_proc_rem_dev(dev);
 		vlan_proc_rem_dev(dev);
-		if (vlan_proc_add_dev(dev) < 0)
-			pr_warn("failed to change proc name for %s\n",
-				dev->name);
+		err = vlan_proc_add_dev(dev);
 		break;
 		break;
 	case NETDEV_REGISTER:
 	case NETDEV_REGISTER:
-		if (vlan_proc_add_dev(dev) < 0)
-			pr_warn("failed to add proc entry for %s\n", dev->name);
+		err = vlan_proc_add_dev(dev);
 		break;
 		break;
 	case NETDEV_UNREGISTER:
 	case NETDEV_UNREGISTER:
 		vlan_proc_rem_dev(dev);
 		vlan_proc_rem_dev(dev);
 		break;
 		break;
 	}
 	}
+
+	return err;
 }
 }
 
 
 static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 static int vlan_device_event(struct notifier_block *unused, unsigned long event,
@@ -356,8 +357,12 @@ static int vlan_device_event(struct notifier_block *unused, unsigned long event,
 	bool last = false;
 	bool last = false;
 	LIST_HEAD(list);
 	LIST_HEAD(list);
 
 
-	if (is_vlan_dev(dev))
-		__vlan_device_event(dev, event);
+	if (is_vlan_dev(dev)) {
+		int err = __vlan_device_event(dev, event);
+
+		if (err)
+			return notifier_from_errno(err);
+	}
 
 
 	if ((event == NETDEV_UP) &&
 	if ((event == NETDEV_UP) &&
 	    (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
 	    (dev->features & NETIF_F_HW_VLAN_CTAG_FILTER)) {

+ 2 - 0
net/8021q/vlanproc.c

@@ -171,6 +171,8 @@ int vlan_proc_add_dev(struct net_device *vlandev)
 	struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
 	struct vlan_dev_priv *vlan = vlan_dev_priv(vlandev);
 	struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id);
 	struct vlan_net *vn = net_generic(dev_net(vlandev), vlan_net_id);
 
 
+	if (!strcmp(vlandev->name, name_conf))
+		return -EINVAL;
 	vlan->dent =
 	vlan->dent =
 		proc_create_data(vlandev->name, S_IFREG|S_IRUSR|S_IWUSR,
 		proc_create_data(vlandev->name, S_IFREG|S_IRUSR|S_IWUSR,
 				 vn->proc_vlan_dir, &vlandev_fops, vlandev);
 				 vn->proc_vlan_dir, &vlandev_fops, vlandev);

+ 27 - 9
net/ipv4/devinet.c

@@ -180,11 +180,12 @@ static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
 static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
 static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
 			 int destroy);
 			 int destroy);
 #ifdef CONFIG_SYSCTL
 #ifdef CONFIG_SYSCTL
-static void devinet_sysctl_register(struct in_device *idev);
+static int devinet_sysctl_register(struct in_device *idev);
 static void devinet_sysctl_unregister(struct in_device *idev);
 static void devinet_sysctl_unregister(struct in_device *idev);
 #else
 #else
-static void devinet_sysctl_register(struct in_device *idev)
+static int devinet_sysctl_register(struct in_device *idev)
 {
 {
+	return 0;
 }
 }
 static void devinet_sysctl_unregister(struct in_device *idev)
 static void devinet_sysctl_unregister(struct in_device *idev)
 {
 {
@@ -232,6 +233,7 @@ EXPORT_SYMBOL(in_dev_finish_destroy);
 static struct in_device *inetdev_init(struct net_device *dev)
 static struct in_device *inetdev_init(struct net_device *dev)
 {
 {
 	struct in_device *in_dev;
 	struct in_device *in_dev;
+	int err = -ENOMEM;
 
 
 	ASSERT_RTNL();
 	ASSERT_RTNL();
 
 
@@ -252,7 +254,13 @@ static struct in_device *inetdev_init(struct net_device *dev)
 	/* Account for reference dev->ip_ptr (below) */
 	/* Account for reference dev->ip_ptr (below) */
 	in_dev_hold(in_dev);
 	in_dev_hold(in_dev);
 
 
-	devinet_sysctl_register(in_dev);
+	err = devinet_sysctl_register(in_dev);
+	if (err) {
+		in_dev->dead = 1;
+		in_dev_put(in_dev);
+		in_dev = NULL;
+		goto out;
+	}
 	ip_mc_init_dev(in_dev);
 	ip_mc_init_dev(in_dev);
 	if (dev->flags & IFF_UP)
 	if (dev->flags & IFF_UP)
 		ip_mc_up(in_dev);
 		ip_mc_up(in_dev);
@@ -260,7 +268,7 @@ static struct in_device *inetdev_init(struct net_device *dev)
 	/* we can receive as soon as ip_ptr is set -- do this last */
 	/* we can receive as soon as ip_ptr is set -- do this last */
 	rcu_assign_pointer(dev->ip_ptr, in_dev);
 	rcu_assign_pointer(dev->ip_ptr, in_dev);
 out:
 out:
-	return in_dev;
+	return in_dev ?: ERR_PTR(err);
 out_kfree:
 out_kfree:
 	kfree(in_dev);
 	kfree(in_dev);
 	in_dev = NULL;
 	in_dev = NULL;
@@ -1347,8 +1355,8 @@ static int inetdev_event(struct notifier_block *this, unsigned long event,
 	if (!in_dev) {
 	if (!in_dev) {
 		if (event == NETDEV_REGISTER) {
 		if (event == NETDEV_REGISTER) {
 			in_dev = inetdev_init(dev);
 			in_dev = inetdev_init(dev);
-			if (!in_dev)
-				return notifier_from_errno(-ENOMEM);
+			if (IS_ERR(in_dev))
+				return notifier_from_errno(PTR_ERR(in_dev));
 			if (dev->flags & IFF_LOOPBACK) {
 			if (dev->flags & IFF_LOOPBACK) {
 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
 				IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
 				IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
@@ -2182,11 +2190,21 @@ static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
 	kfree(t);
 	kfree(t);
 }
 }
 
 
-static void devinet_sysctl_register(struct in_device *idev)
+static int devinet_sysctl_register(struct in_device *idev)
 {
 {
-	neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
-	__devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
+	int err;
+
+	if (!sysctl_dev_name_is_allowed(idev->dev->name))
+		return -EINVAL;
+
+	err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL);
+	if (err)
+		return err;
+	err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
 					&idev->cnf);
 					&idev->cnf);
+	if (err)
+		neigh_sysctl_unregister(idev->arp_parms);
+	return err;
 }
 }
 
 
 static void devinet_sysctl_unregister(struct in_device *idev)
 static void devinet_sysctl_unregister(struct in_device *idev)

+ 54 - 28
net/ipv6/addrconf.c

@@ -108,11 +108,12 @@ static inline u32 cstamp_delta(unsigned long cstamp)
 }
 }
 
 
 #ifdef CONFIG_SYSCTL
 #ifdef CONFIG_SYSCTL
-static void addrconf_sysctl_register(struct inet6_dev *idev);
+static int addrconf_sysctl_register(struct inet6_dev *idev);
 static void addrconf_sysctl_unregister(struct inet6_dev *idev);
 static void addrconf_sysctl_unregister(struct inet6_dev *idev);
 #else
 #else
-static inline void addrconf_sysctl_register(struct inet6_dev *idev)
+static inline int addrconf_sysctl_register(struct inet6_dev *idev)
 {
 {
+	return 0;
 }
 }
 
 
 static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
 static inline void addrconf_sysctl_unregister(struct inet6_dev *idev)
@@ -310,16 +311,16 @@ err_ip:
 static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 {
 {
 	struct inet6_dev *ndev;
 	struct inet6_dev *ndev;
+	int err = -ENOMEM;
 
 
 	ASSERT_RTNL();
 	ASSERT_RTNL();
 
 
 	if (dev->mtu < IPV6_MIN_MTU)
 	if (dev->mtu < IPV6_MIN_MTU)
-		return NULL;
+		return ERR_PTR(-EINVAL);
 
 
 	ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
 	ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
-
 	if (ndev == NULL)
 	if (ndev == NULL)
-		return NULL;
+		return ERR_PTR(err);
 
 
 	rwlock_init(&ndev->lock);
 	rwlock_init(&ndev->lock);
 	ndev->dev = dev;
 	ndev->dev = dev;
@@ -332,7 +333,7 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 	ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
 	ndev->nd_parms = neigh_parms_alloc(dev, &nd_tbl);
 	if (ndev->nd_parms == NULL) {
 	if (ndev->nd_parms == NULL) {
 		kfree(ndev);
 		kfree(ndev);
-		return NULL;
+		return ERR_PTR(err);
 	}
 	}
 	if (ndev->cnf.forwarding)
 	if (ndev->cnf.forwarding)
 		dev_disable_lro(dev);
 		dev_disable_lro(dev);
@@ -346,17 +347,14 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 		neigh_parms_release(&nd_tbl, ndev->nd_parms);
 		neigh_parms_release(&nd_tbl, ndev->nd_parms);
 		dev_put(dev);
 		dev_put(dev);
 		kfree(ndev);
 		kfree(ndev);
-		return NULL;
+		return ERR_PTR(err);
 	}
 	}
 
 
 	if (snmp6_register_dev(ndev) < 0) {
 	if (snmp6_register_dev(ndev) < 0) {
 		ADBG(KERN_WARNING
 		ADBG(KERN_WARNING
 			"%s: cannot create /proc/net/dev_snmp6/%s\n",
 			"%s: cannot create /proc/net/dev_snmp6/%s\n",
 			__func__, dev->name);
 			__func__, dev->name);
-		neigh_parms_release(&nd_tbl, ndev->nd_parms);
-		ndev->dead = 1;
-		in6_dev_finish_destroy(ndev);
-		return NULL;
+		goto err_release;
 	}
 	}
 
 
 	/* One reference from device.  We must do this before
 	/* One reference from device.  We must do this before
@@ -394,7 +392,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 
 
 	ipv6_mc_init_dev(ndev);
 	ipv6_mc_init_dev(ndev);
 	ndev->tstamp = jiffies;
 	ndev->tstamp = jiffies;
-	addrconf_sysctl_register(ndev);
+	err = addrconf_sysctl_register(ndev);
+	if (err) {
+		ipv6_mc_destroy_dev(ndev);
+		del_timer(&ndev->regen_timer);
+		goto err_release;
+	}
 	/* protected by rtnl_lock */
 	/* protected by rtnl_lock */
 	rcu_assign_pointer(dev->ip6_ptr, ndev);
 	rcu_assign_pointer(dev->ip6_ptr, ndev);
 
 
@@ -409,6 +412,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 		ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
 		ipv6_dev_mc_inc(dev, &in6addr_linklocal_allrouters);
 
 
 	return ndev;
 	return ndev;
+
+err_release:
+	neigh_parms_release(&nd_tbl, ndev->nd_parms);
+	ndev->dead = 1;
+	in6_dev_finish_destroy(ndev);
+	return ERR_PTR(err);
 }
 }
 
 
 static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
 static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
@@ -420,7 +429,7 @@ static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
 	idev = __in6_dev_get(dev);
 	idev = __in6_dev_get(dev);
 	if (!idev) {
 	if (!idev) {
 		idev = ipv6_add_dev(dev);
 		idev = ipv6_add_dev(dev);
-		if (!idev)
+		if (IS_ERR(idev))
 			return NULL;
 			return NULL;
 	}
 	}
 
 
@@ -2830,8 +2839,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
 	case NETDEV_REGISTER:
 	case NETDEV_REGISTER:
 		if (!idev && dev->mtu >= IPV6_MIN_MTU) {
 		if (!idev && dev->mtu >= IPV6_MIN_MTU) {
 			idev = ipv6_add_dev(dev);
 			idev = ipv6_add_dev(dev);
-			if (!idev)
-				return notifier_from_errno(-ENOMEM);
+			if (IS_ERR(idev))
+				return notifier_from_errno(PTR_ERR(idev));
 		}
 		}
 		break;
 		break;
 
 
@@ -2851,7 +2860,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
 			if (!idev && dev->mtu >= IPV6_MIN_MTU)
 			if (!idev && dev->mtu >= IPV6_MIN_MTU)
 				idev = ipv6_add_dev(dev);
 				idev = ipv6_add_dev(dev);
 
 
-			if (idev) {
+			if (!IS_ERR_OR_NULL(idev)) {
 				idev->if_flags |= IF_READY;
 				idev->if_flags |= IF_READY;
 				run_pending = 1;
 				run_pending = 1;
 			}
 			}
@@ -2894,7 +2903,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
 			break;
 			break;
 		}
 		}
 
 
-		if (idev) {
+		if (!IS_ERR_OR_NULL(idev)) {
 			if (run_pending)
 			if (run_pending)
 				addrconf_dad_run(idev);
 				addrconf_dad_run(idev);
 
 
@@ -2929,7 +2938,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
 
 
 		if (!idev && dev->mtu >= IPV6_MIN_MTU) {
 		if (!idev && dev->mtu >= IPV6_MIN_MTU) {
 			idev = ipv6_add_dev(dev);
 			idev = ipv6_add_dev(dev);
-			if (idev)
+			if (!IS_ERR(idev))
 				break;
 				break;
 		}
 		}
 
 
@@ -2950,10 +2959,14 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
 		if (idev) {
 		if (idev) {
 			snmp6_unregister_dev(idev);
 			snmp6_unregister_dev(idev);
 			addrconf_sysctl_unregister(idev);
 			addrconf_sysctl_unregister(idev);
-			addrconf_sysctl_register(idev);
-			err = snmp6_register_dev(idev);
+			err = addrconf_sysctl_register(idev);
 			if (err)
 			if (err)
 				return notifier_from_errno(err);
 				return notifier_from_errno(err);
+			err = snmp6_register_dev(idev);
+			if (err) {
+				addrconf_sysctl_unregister(idev);
+				return notifier_from_errno(err);
+			}
 		}
 		}
 		break;
 		break;
 
 
@@ -5248,12 +5261,23 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
 	kfree(t);
 	kfree(t);
 }
 }
 
 
-static void addrconf_sysctl_register(struct inet6_dev *idev)
+static int addrconf_sysctl_register(struct inet6_dev *idev)
 {
 {
-	neigh_sysctl_register(idev->dev, idev->nd_parms,
-			      &ndisc_ifinfo_sysctl_change);
-	__addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
-					idev, &idev->cnf);
+	int err;
+
+	if (!sysctl_dev_name_is_allowed(idev->dev->name))
+		return -EINVAL;
+
+	err = neigh_sysctl_register(idev->dev, idev->nd_parms,
+				    &ndisc_ifinfo_sysctl_change);
+	if (err)
+		return err;
+	err = __addrconf_sysctl_register(dev_net(idev->dev), idev->dev->name,
+					 idev, &idev->cnf);
+	if (err)
+		neigh_sysctl_unregister(idev->nd_parms);
+
+	return err;
 }
 }
 
 
 static void addrconf_sysctl_unregister(struct inet6_dev *idev)
 static void addrconf_sysctl_unregister(struct inet6_dev *idev)
@@ -5338,6 +5362,7 @@ static struct rtnl_af_ops inet6_ops = {
 
 
 int __init addrconf_init(void)
 int __init addrconf_init(void)
 {
 {
+	struct inet6_dev *idev;
 	int i, err;
 	int i, err;
 
 
 	err = ipv6_addr_label_init();
 	err = ipv6_addr_label_init();
@@ -5376,11 +5401,12 @@ int __init addrconf_init(void)
 	 * device and it being up should be removed.
 	 * device and it being up should be removed.
 	 */
 	 */
 	rtnl_lock();
 	rtnl_lock();
-	if (!ipv6_add_dev(init_net.loopback_dev))
-		err = -ENOMEM;
+	idev = ipv6_add_dev(init_net.loopback_dev);
 	rtnl_unlock();
 	rtnl_unlock();
-	if (err)
+	if (IS_ERR(idev)) {
+		err = PTR_ERR(idev);
 		goto errlo;
 		goto errlo;
+	}
 
 
 	for (i = 0; i < IN6_ADDR_HSIZE; i++)
 	for (i = 0; i < IN6_ADDR_HSIZE; i++)
 		INIT_HLIST_HEAD(&inet6_addr_lst[i]);
 		INIT_HLIST_HEAD(&inet6_addr_lst[i]);