diff options
author | WANG Cong <xiyou.wangcong@gmail.com> | 2016-10-20 02:35:12 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-10-21 11:29:02 -0400 |
commit | 8651be8f14a12d24f203f283601d9b0418c389ff (patch) | |
tree | 15f086903cd41889f29e23ac2f80d8bb77f7ead4 /net | |
parent | 8dbad1a81128f7e224fce3cce28a1d545d9c0b88 (diff) |
ipv6: fix a potential deadlock in do_ipv6_setsockopt()
Baozeng reported this deadlock case:
CPU0 CPU1
---- ----
lock([ 165.136033] sk_lock-AF_INET6);
lock([ 165.136033] rtnl_mutex);
lock([ 165.136033] sk_lock-AF_INET6);
lock([ 165.136033] rtnl_mutex);
Similar to commit 87e9f0315952
("ipv4: fix a potential deadlock in mcast getsockopt() path")
this is due to we still have a case, ipv6_sock_mc_close(),
where we acquire sk_lock before rtnl_lock. Close this deadlock
with the similar solution, that is always acquire rtnl lock first.
Fixes: baf606d9c9b1 ("ipv4,ipv6: grab rtnl before locking the socket")
Reported-by: Baozeng Ding <sploving1@gmail.com>
Tested-by: Baozeng Ding <sploving1@gmail.com>
Cc: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Cong Wang <xiyou.wangcong@gmail.com>
Reviewed-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 3 | ||||
-rw-r--r-- | net/ipv6/mcast.c | 17 |
2 files changed, 14 insertions, 6 deletions
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 5330262ab673..636ec56f5f50 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -120,6 +120,7 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk, | |||
120 | static bool setsockopt_needs_rtnl(int optname) | 120 | static bool setsockopt_needs_rtnl(int optname) |
121 | { | 121 | { |
122 | switch (optname) { | 122 | switch (optname) { |
123 | case IPV6_ADDRFORM: | ||
123 | case IPV6_ADD_MEMBERSHIP: | 124 | case IPV6_ADD_MEMBERSHIP: |
124 | case IPV6_DROP_MEMBERSHIP: | 125 | case IPV6_DROP_MEMBERSHIP: |
125 | case IPV6_JOIN_ANYCAST: | 126 | case IPV6_JOIN_ANYCAST: |
@@ -198,7 +199,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, | |||
198 | } | 199 | } |
199 | 200 | ||
200 | fl6_free_socklist(sk); | 201 | fl6_free_socklist(sk); |
201 | ipv6_sock_mc_close(sk); | 202 | __ipv6_sock_mc_close(sk); |
202 | 203 | ||
203 | /* | 204 | /* |
204 | * Sock is moving from IPv6 to IPv4 (sk_prot), so | 205 | * Sock is moving from IPv6 to IPv4 (sk_prot), so |
diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 75c1fc54f188..14a3903f1c82 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c | |||
@@ -276,16 +276,14 @@ static struct inet6_dev *ip6_mc_find_dev_rcu(struct net *net, | |||
276 | return idev; | 276 | return idev; |
277 | } | 277 | } |
278 | 278 | ||
279 | void ipv6_sock_mc_close(struct sock *sk) | 279 | void __ipv6_sock_mc_close(struct sock *sk) |
280 | { | 280 | { |
281 | struct ipv6_pinfo *np = inet6_sk(sk); | 281 | struct ipv6_pinfo *np = inet6_sk(sk); |
282 | struct ipv6_mc_socklist *mc_lst; | 282 | struct ipv6_mc_socklist *mc_lst; |
283 | struct net *net = sock_net(sk); | 283 | struct net *net = sock_net(sk); |
284 | 284 | ||
285 | if (!rcu_access_pointer(np->ipv6_mc_list)) | 285 | ASSERT_RTNL(); |
286 | return; | ||
287 | 286 | ||
288 | rtnl_lock(); | ||
289 | while ((mc_lst = rtnl_dereference(np->ipv6_mc_list)) != NULL) { | 287 | while ((mc_lst = rtnl_dereference(np->ipv6_mc_list)) != NULL) { |
290 | struct net_device *dev; | 288 | struct net_device *dev; |
291 | 289 | ||
@@ -303,8 +301,17 @@ void ipv6_sock_mc_close(struct sock *sk) | |||
303 | 301 | ||
304 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); | 302 | atomic_sub(sizeof(*mc_lst), &sk->sk_omem_alloc); |
305 | kfree_rcu(mc_lst, rcu); | 303 | kfree_rcu(mc_lst, rcu); |
306 | |||
307 | } | 304 | } |
305 | } | ||
306 | |||
307 | void ipv6_sock_mc_close(struct sock *sk) | ||
308 | { | ||
309 | struct ipv6_pinfo *np = inet6_sk(sk); | ||
310 | |||
311 | if (!rcu_access_pointer(np->ipv6_mc_list)) | ||
312 | return; | ||
313 | rtnl_lock(); | ||
314 | __ipv6_sock_mc_close(sk); | ||
308 | rtnl_unlock(); | 315 | rtnl_unlock(); |
309 | } | 316 | } |
310 | 317 | ||