]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
net/ipv6: avoid possible dead locking on addr_gen_mode sysctl
authorFelix Jia <felix.jia@alliedtelesis.co.nz>
Sun, 26 Feb 2017 23:41:23 +0000 (12:41 +1300)
committerDavid S. Miller <davem@davemloft.net>
Wed, 1 Mar 2017 18:22:48 +0000 (10:22 -0800)
The addr_gen_mode variable can be accessed by both sysctl and netlink.
Repleacd rtnl_lock() with rtnl_trylock() protect the sysctl operation to
avoid the possbile dead lock.`

Signed-off-by: Felix Jia <felix.jia@alliedtelesis.co.nz>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/ipv6/addrconf.c

index 3a2025f5bf2c333a37d18329cdec88fdc1827870..cfc485a8e1c0286ae98d723e3858171090673ddd 100644 (file)
@@ -5692,13 +5692,18 @@ static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
        struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1;
        struct net *net = (struct net *)ctl->extra2;
 
+       if (!rtnl_trylock())
+               return restart_syscall();
+
        ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
 
        if (write) {
                new_val = *((int *)ctl->data);
 
-               if (check_addr_gen_mode(new_val) < 0)
-                       return -EINVAL;
+               if (check_addr_gen_mode(new_val) < 0) {
+                       ret = -EINVAL;
+                       goto out;
+               }
 
                /* request for default */
                if (&net->ipv6.devconf_dflt->addr_gen_mode == ctl->data) {
@@ -5707,20 +5712,23 @@ static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write,
                /* request for individual net device */
                } else {
                        if (!idev)
-                               return ret;
+                               goto out;
 
-                       if (check_stable_privacy(idev, net, new_val) < 0)
-                               return -EINVAL;
+                       if (check_stable_privacy(idev, net, new_val) < 0) {
+                               ret = -EINVAL;
+                               goto out;
+                       }
 
                        if (idev->cnf.addr_gen_mode != new_val) {
                                idev->cnf.addr_gen_mode = new_val;
-                               rtnl_lock();
                                addrconf_dev_config(idev->dev);
-                               rtnl_unlock();
                        }
                }
        }
 
+out:
+       rtnl_unlock();
+
        return ret;
 }