ipvs: call rtnl_lock early
authorJulian Anastasov <ja@ssi.bg>
Sun, 26 Jul 2015 11:57:34 +0000 (14:57 +0300)
committerSimon Horman <horms@verge.net.au>
Fri, 21 Aug 2015 16:09:09 +0000 (09:09 -0700)
When the sync damon is started we need to hold rtnl
lock while calling ip_mc_join_group. Currently, we have
a wrong locking order because the correct one is
rtnl_lock->__ip_vs_mutex. It is implied from the usage
of __ip_vs_mutex in ip_vs_dst_event() which is called
under rtnl lock during NETDEV_* notifications.

Fix the problem by calling rtnl_lock early only for the
start_sync_thread call. As a bonus this fixes the usage
__dev_get_by_name which was not called under rtnl lock.

This patch actually extends and depends on commit 54ff9ef36bdf
("ipv4, ipv6: kill ip_mc_{join, leave}_group and
ipv6_sock_mc_{join, drop}").

Signed-off-by: Julian Anastasov <ja@ssi.bg>
Signed-off-by: Simon Horman <horms@verge.net.au>
net/netfilter/ipvs/ip_vs_ctl.c
net/netfilter/ipvs/ip_vs_sync.c

index 24c554201a766d175ed90359b6b78617acd37c64..af0b69e411b7e0fc37923872e42c144c0e68391e 100644 (file)
@@ -2335,13 +2335,18 @@ do_ip_vs_set_ctl(struct sock *sk, int cmd, void __user *user, unsigned int len)
            cmd == IP_VS_SO_SET_STOPDAEMON) {
                struct ip_vs_daemon_user *dm = (struct ip_vs_daemon_user *)arg;
 
-               mutex_lock(&ipvs->sync_mutex);
-               if (cmd == IP_VS_SO_SET_STARTDAEMON)
+               if (cmd == IP_VS_SO_SET_STARTDAEMON) {
+                       rtnl_lock();
+                       mutex_lock(&ipvs->sync_mutex);
                        ret = start_sync_thread(net, dm->state, dm->mcast_ifn,
                                                dm->syncid);
-               else
+                       mutex_unlock(&ipvs->sync_mutex);
+                       rtnl_unlock();
+               } else {
+                       mutex_lock(&ipvs->sync_mutex);
                        ret = stop_sync_thread(net, dm->state);
-               mutex_unlock(&ipvs->sync_mutex);
+                       mutex_unlock(&ipvs->sync_mutex);
+               }
                goto out_dec;
        }
 
@@ -3342,6 +3347,9 @@ nla_put_failure:
 
 static int ip_vs_genl_new_daemon(struct net *net, struct nlattr **attrs)
 {
+       struct netns_ipvs *ipvs = net_ipvs(net);
+       int ret;
+
        if (!(attrs[IPVS_DAEMON_ATTR_STATE] &&
              attrs[IPVS_DAEMON_ATTR_MCAST_IFN] &&
              attrs[IPVS_DAEMON_ATTR_SYNC_ID]))
@@ -3353,19 +3361,30 @@ static int ip_vs_genl_new_daemon(struct net *net, struct nlattr **attrs)
        if (net_ipvs(net)->mixed_address_family_dests > 0)
                return -EINVAL;
 
-       return start_sync_thread(net,
-                                nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
-                                nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
-                                nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
+       rtnl_lock();
+       mutex_lock(&ipvs->sync_mutex);
+       ret = start_sync_thread(net,
+                               nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]),
+                               nla_data(attrs[IPVS_DAEMON_ATTR_MCAST_IFN]),
+                               nla_get_u32(attrs[IPVS_DAEMON_ATTR_SYNC_ID]));
+       mutex_unlock(&ipvs->sync_mutex);
+       rtnl_unlock();
+       return ret;
 }
 
 static int ip_vs_genl_del_daemon(struct net *net, struct nlattr **attrs)
 {
+       struct netns_ipvs *ipvs = net_ipvs(net);
+       int ret;
+
        if (!attrs[IPVS_DAEMON_ATTR_STATE])
                return -EINVAL;
 
-       return stop_sync_thread(net,
-                               nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
+       mutex_lock(&ipvs->sync_mutex);
+       ret = stop_sync_thread(net,
+                              nla_get_u32(attrs[IPVS_DAEMON_ATTR_STATE]));
+       mutex_unlock(&ipvs->sync_mutex);
+       return ret;
 }
 
 static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs)
@@ -3389,7 +3408,7 @@ static int ip_vs_genl_set_config(struct net *net, struct nlattr **attrs)
 
 static int ip_vs_genl_set_daemon(struct sk_buff *skb, struct genl_info *info)
 {
-       int ret = 0, cmd;
+       int ret = -EINVAL, cmd;
        struct net *net;
        struct netns_ipvs *ipvs;
 
@@ -3400,22 +3419,19 @@ static int ip_vs_genl_set_daemon(struct sk_buff *skb, struct genl_info *info)
        if (cmd == IPVS_CMD_NEW_DAEMON || cmd == IPVS_CMD_DEL_DAEMON) {
                struct nlattr *daemon_attrs[IPVS_DAEMON_ATTR_MAX + 1];
 
-               mutex_lock(&ipvs->sync_mutex);
                if (!info->attrs[IPVS_CMD_ATTR_DAEMON] ||
                    nla_parse_nested(daemon_attrs, IPVS_DAEMON_ATTR_MAX,
                                     info->attrs[IPVS_CMD_ATTR_DAEMON],
-                                    ip_vs_daemon_policy)) {
-                       ret = -EINVAL;
+                                    ip_vs_daemon_policy))
                        goto out;
-               }
 
                if (cmd == IPVS_CMD_NEW_DAEMON)
                        ret = ip_vs_genl_new_daemon(net, daemon_attrs);
                else
                        ret = ip_vs_genl_del_daemon(net, daemon_attrs);
-out:
-               mutex_unlock(&ipvs->sync_mutex);
        }
+
+out:
        return ret;
 }
 
index d99ad93eb85508594cc5f6919a0b9eb005f19b33..6bc6dca9bca8bf050ae27724e1c7e34596b9cc38 100644 (file)
@@ -1405,11 +1405,9 @@ join_mcast_group(struct sock *sk, struct in_addr *addr, char *ifname)
 
        mreq.imr_ifindex = dev->ifindex;
 
-       rtnl_lock();
        lock_sock(sk);
        ret = ip_mc_join_group(sk, &mreq);
        release_sock(sk);
-       rtnl_unlock();
 
        return ret;
 }