Merge branch 'stable-3.17' of git://git.infradead.org/users/pcmoore/selinux
[firefly-linux-kernel-4.4.55.git] / net / ipv6 / addrconf.c
index 4c03c28430945930a4aaacc5b55742b7f7488bf9..0b239fc1816ed828862f19cdde0f08720a0b43ca 100644 (file)
@@ -108,11 +108,12 @@ static inline u32 cstamp_delta(unsigned long cstamp)
 }
 
 #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);
 #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)
@@ -310,16 +311,16 @@ err_ip:
 static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 {
        struct inet6_dev *ndev;
+       int err = -ENOMEM;
 
        ASSERT_RTNL();
 
        if (dev->mtu < IPV6_MIN_MTU)
-               return NULL;
+               return ERR_PTR(-EINVAL);
 
        ndev = kzalloc(sizeof(struct inet6_dev), GFP_KERNEL);
-
        if (ndev == NULL)
-               return NULL;
+               return ERR_PTR(err);
 
        rwlock_init(&ndev->lock);
        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);
        if (ndev->nd_parms == NULL) {
                kfree(ndev);
-               return NULL;
+               return ERR_PTR(err);
        }
        if (ndev->cnf.forwarding)
                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);
                dev_put(dev);
                kfree(ndev);
-               return NULL;
+               return ERR_PTR(err);
        }
 
        if (snmp6_register_dev(ndev) < 0) {
                ADBG(KERN_WARNING
                        "%s: cannot create /proc/net/dev_snmp6/%s\n",
                        __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
@@ -394,7 +392,12 @@ static struct inet6_dev *ipv6_add_dev(struct net_device *dev)
 
        ipv6_mc_init_dev(ndev);
        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 */
        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);
 
        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)
@@ -420,7 +429,7 @@ static struct inet6_dev *ipv6_find_idev(struct net_device *dev)
        idev = __in6_dev_get(dev);
        if (!idev) {
                idev = ipv6_add_dev(dev);
-               if (!idev)
+               if (IS_ERR(idev))
                        return NULL;
        }
 
@@ -2830,8 +2839,8 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
        case NETDEV_REGISTER:
                if (!idev && dev->mtu >= IPV6_MIN_MTU) {
                        idev = ipv6_add_dev(dev);
-                       if (!idev)
-                               return notifier_from_errno(-ENOMEM);
+                       if (IS_ERR(idev))
+                               return notifier_from_errno(PTR_ERR(idev));
                }
                break;
 
@@ -2851,7 +2860,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                        if (!idev && dev->mtu >= IPV6_MIN_MTU)
                                idev = ipv6_add_dev(dev);
 
-                       if (idev) {
+                       if (!IS_ERR_OR_NULL(idev)) {
                                idev->if_flags |= IF_READY;
                                run_pending = 1;
                        }
@@ -2894,7 +2903,7 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                        break;
                }
 
-               if (idev) {
+               if (!IS_ERR_OR_NULL(idev)) {
                        if (run_pending)
                                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) {
                        idev = ipv6_add_dev(dev);
-                       if (idev)
+                       if (!IS_ERR(idev))
                                break;
                }
 
@@ -2950,10 +2959,14 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event,
                if (idev) {
                        snmp6_unregister_dev(idev);
                        addrconf_sysctl_unregister(idev);
-                       addrconf_sysctl_register(idev);
-                       err = snmp6_register_dev(idev);
+                       err = addrconf_sysctl_register(idev);
                        if (err)
                                return notifier_from_errno(err);
+                       err = snmp6_register_dev(idev);
+                       if (err) {
+                               addrconf_sysctl_unregister(idev);
+                               return notifier_from_errno(err);
+                       }
                }
                break;
 
@@ -5248,12 +5261,23 @@ static void __addrconf_sysctl_unregister(struct ipv6_devconf *p)
        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)
@@ -5338,6 +5362,7 @@ static struct rtnl_af_ops inet6_ops = {
 
 int __init addrconf_init(void)
 {
+       struct inet6_dev *idev;
        int i, err;
 
        err = ipv6_addr_label_init();
@@ -5376,11 +5401,12 @@ int __init addrconf_init(void)
         * device and it being up should be removed.
         */
        rtnl_lock();
-       if (!ipv6_add_dev(init_net.loopback_dev))
-               err = -ENOMEM;
+       idev = ipv6_add_dev(init_net.loopback_dev);
        rtnl_unlock();
-       if (err)
+       if (IS_ERR(idev)) {
+               err = PTR_ERR(idev);
                goto errlo;
+       }
 
        for (i = 0; i < IN6_ADDR_HSIZE; i++)
                INIT_HLIST_HEAD(&inet6_addr_lst[i]);