diff options
author | Denis Lunev <den@openvz.org> | 2007-04-18 20:05:58 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-04-18 20:05:58 -0400 |
commit | ac57b3a9ce280763296f99e32187a0b4384d9389 (patch) | |
tree | 5aa81b60abfe3436a5d7d52a93a9c5cb9b9d71a5 /net/netlink | |
parent | bfb6709d0b239af5e3ce5859aae926e1b79ba84b (diff) |
[NETLINK]: Don't attach callback to a going-away netlink socket
There is a race between netlink_dump_start() and netlink_release()
that can lead to the situation when a netlink socket with non-zero
callback is freed.
Here it is:
CPU1: CPU2
netlink_release(): netlink_dump_start():
sk = netlink_lookup(); /* OK */
netlink_remove();
spin_lock(&nlk->cb_lock);
if (nlk->cb) { /* false */
...
}
spin_unlock(&nlk->cb_lock);
spin_lock(&nlk->cb_lock);
if (nlk->cb) { /* false */
...
}
nlk->cb = cb;
spin_unlock(&nlk->cb_lock);
...
sock_orphan(sk);
/*
* proceed with releasing
* the socket
*/
The proposal it to make sock_orphan before detaching the callback
in netlink_release() and to check for the sock to be SOCK_DEAD in
netlink_dump_start() before setting a new callback.
Signed-off-by: Denis Lunev <den@openvz.org>
Signed-off-by: Kirill Korotaev <dev@openvz.org>
Signed-off-by: Pavel Emelianov <xemul@openvz.org>
Acked-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink')
-rw-r--r-- | net/netlink/af_netlink.c | 6 |
1 files changed, 3 insertions, 3 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index e73d8f546c6b..c48b0f49f003 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -443,6 +443,7 @@ static int netlink_release(struct socket *sock) | |||
443 | return 0; | 443 | return 0; |
444 | 444 | ||
445 | netlink_remove(sk); | 445 | netlink_remove(sk); |
446 | sock_orphan(sk); | ||
446 | nlk = nlk_sk(sk); | 447 | nlk = nlk_sk(sk); |
447 | 448 | ||
448 | spin_lock(&nlk->cb_lock); | 449 | spin_lock(&nlk->cb_lock); |
@@ -457,7 +458,6 @@ static int netlink_release(struct socket *sock) | |||
457 | /* OK. Socket is unlinked, and, therefore, | 458 | /* OK. Socket is unlinked, and, therefore, |
458 | no new packets will arrive */ | 459 | no new packets will arrive */ |
459 | 460 | ||
460 | sock_orphan(sk); | ||
461 | sock->sk = NULL; | 461 | sock->sk = NULL; |
462 | wake_up_interruptible_all(&nlk->wait); | 462 | wake_up_interruptible_all(&nlk->wait); |
463 | 463 | ||
@@ -1412,9 +1412,9 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, | |||
1412 | return -ECONNREFUSED; | 1412 | return -ECONNREFUSED; |
1413 | } | 1413 | } |
1414 | nlk = nlk_sk(sk); | 1414 | nlk = nlk_sk(sk); |
1415 | /* A dump is in progress... */ | 1415 | /* A dump or destruction is in progress... */ |
1416 | spin_lock(&nlk->cb_lock); | 1416 | spin_lock(&nlk->cb_lock); |
1417 | if (nlk->cb) { | 1417 | if (nlk->cb || sock_flag(sk, SOCK_DEAD)) { |
1418 | spin_unlock(&nlk->cb_lock); | 1418 | spin_unlock(&nlk->cb_lock); |
1419 | netlink_destroy_callback(cb); | 1419 | netlink_destroy_callback(cb); |
1420 | sock_put(sk); | 1420 | sock_put(sk); |