net: Handle unregister properly when netdev namespace change fails.
[linux-drm-fsl-dcu.git] / net / core / rtnetlink.c
index ab293a3066b34bc4f6af71701f0c12b9ab6e5a34..ee0608bb3bc087212d12bac729677bf454053560 100644 (file)
@@ -1300,7 +1300,6 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        s_h = cb->args[0];
        s_idx = cb->args[1];
 
-       rcu_read_lock();
        cb->seq = net->dev_base_seq;
 
        /* A hack to preserve kernel<->userspace interface.
@@ -1322,7 +1321,7 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb)
        for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
                idx = 0;
                head = &net->dev_index_head[h];
-               hlist_for_each_entry_rcu(dev, head, index_hlist) {
+               hlist_for_each_entry(dev, head, index_hlist) {
                        if (idx < s_idx)
                                goto cont;
                        err = rtnl_fill_ifinfo(skb, dev, RTM_NEWLINK,
@@ -1344,7 +1343,6 @@ cont:
                }
        }
 out:
-       rcu_read_unlock();
        cb->args[1] = idx;
        cb->args[0] = h;
 
@@ -2012,8 +2010,8 @@ replay:
        }
 
        if (1) {
-               struct nlattr *attr[ops ? ops->maxtype + 1 : 0];
-               struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 0];
+               struct nlattr *attr[ops ? ops->maxtype + 1 : 1];
+               struct nlattr *slave_attr[m_ops ? m_ops->slave_maxtype + 1 : 1];
                struct nlattr **data = NULL;
                struct nlattr **slave_data = NULL;
                struct net *dest_net, *link_net = NULL;
@@ -2122,6 +2120,10 @@ replay:
                if (IS_ERR(dest_net))
                        return PTR_ERR(dest_net);
 
+               err = -EPERM;
+               if (!netlink_ns_capable(skb, dest_net->user_ns, CAP_NET_ADMIN))
+                       goto out;
+
                if (tb[IFLA_LINK_NETNSID]) {
                        int id = nla_get_s32(tb[IFLA_LINK_NETNSID]);
 
@@ -2130,6 +2132,9 @@ replay:
                                err =  -EINVAL;
                                goto out;
                        }
+                       err = -EPERM;
+                       if (!netlink_ns_capable(skb, link_net->user_ns, CAP_NET_ADMIN))
+                               goto out;
                }
 
                dev = rtnl_create_link(link_net ? : dest_net, ifname,
@@ -2161,28 +2166,28 @@ replay:
                        }
                }
                err = rtnl_configure_link(dev, ifm);
-               if (err < 0) {
-                       if (ops->newlink) {
-                               LIST_HEAD(list_kill);
-
-                               ops->dellink(dev, &list_kill);
-                               unregister_netdevice_many(&list_kill);
-                       } else {
-                               unregister_netdevice(dev);
-                       }
-                       goto out;
-               }
-
+               if (err < 0)
+                       goto out_unregister;
                if (link_net) {
                        err = dev_change_net_namespace(dev, dest_net, ifname);
                        if (err < 0)
-                               unregister_netdevice(dev);
+                               goto out_unregister;
                }
 out:
                if (link_net)
                        put_net(link_net);
                put_net(dest_net);
                return err;
+out_unregister:
+               if (ops->newlink) {
+                       LIST_HEAD(list_kill);
+
+                       ops->dellink(dev, &list_kill);
+                       unregister_netdevice_many(&list_kill);
+               } else {
+                       unregister_netdevice(dev);
+               }
+               goto out;
        }
 }