diff options
author | Felix Jia <felix.jia@alliedtelesis.co.nz> | 2017-02-26 18:41:23 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-03-01 13:22:48 -0500 |
commit | 8c171d6ca56c6891372a97af26b58b2cfad7fd9a (patch) | |
tree | 13aaa3cbaebb1e12014a986f9f5e2c7c944501b1 /net | |
parent | a3695e9751b3c616170b552f8561b5a7c5b7a3ca (diff) |
net/ipv6: avoid possible dead locking on addr_gen_mode sysctl
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/addrconf.c | 22 |
1 files changed, 15 insertions, 7 deletions
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3a2025f5bf2c..cfc485a8e1c0 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c | |||
@@ -5692,13 +5692,18 @@ static int addrconf_sysctl_addr_gen_mode(struct ctl_table *ctl, int write, | |||
5692 | struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1; | 5692 | struct inet6_dev *idev = (struct inet6_dev *)ctl->extra1; |
5693 | struct net *net = (struct net *)ctl->extra2; | 5693 | struct net *net = (struct net *)ctl->extra2; |
5694 | 5694 | ||
5695 | if (!rtnl_trylock()) | ||
5696 | return restart_syscall(); | ||
5697 | |||
5695 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); | 5698 | ret = proc_dointvec(ctl, write, buffer, lenp, ppos); |
5696 | 5699 | ||
5697 | if (write) { | 5700 | if (write) { |
5698 | new_val = *((int *)ctl->data); | 5701 | new_val = *((int *)ctl->data); |
5699 | 5702 | ||
5700 | if (check_addr_gen_mode(new_val) < 0) | 5703 | if (check_addr_gen_mode(new_val) < 0) { |
5701 | return -EINVAL; | 5704 | ret = -EINVAL; |
5705 | goto out; | ||
5706 | } | ||
5702 | 5707 | ||
5703 | /* request for default */ | 5708 | /* request for default */ |
5704 | if (&net->ipv6.devconf_dflt->addr_gen_mode == ctl->data) { | 5709 | 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, | |||
5707 | /* request for individual net device */ | 5712 | /* request for individual net device */ |
5708 | } else { | 5713 | } else { |
5709 | if (!idev) | 5714 | if (!idev) |
5710 | return ret; | 5715 | goto out; |
5711 | 5716 | ||
5712 | if (check_stable_privacy(idev, net, new_val) < 0) | 5717 | if (check_stable_privacy(idev, net, new_val) < 0) { |
5713 | return -EINVAL; | 5718 | ret = -EINVAL; |
5719 | goto out; | ||
5720 | } | ||
5714 | 5721 | ||
5715 | if (idev->cnf.addr_gen_mode != new_val) { | 5722 | if (idev->cnf.addr_gen_mode != new_val) { |
5716 | idev->cnf.addr_gen_mode = new_val; | 5723 | idev->cnf.addr_gen_mode = new_val; |
5717 | rtnl_lock(); | ||
5718 | addrconf_dev_config(idev->dev); | 5724 | addrconf_dev_config(idev->dev); |
5719 | rtnl_unlock(); | ||
5720 | } | 5725 | } |
5721 | } | 5726 | } |
5722 | } | 5727 | } |
5723 | 5728 | ||
5729 | out: | ||
5730 | rtnl_unlock(); | ||
5731 | |||
5724 | return ret; | 5732 | return ret; |
5725 | } | 5733 | } |
5726 | 5734 | ||