Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[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>

authored by

Denis Lunev and committed by
David S. Miller
ac57b3a9 bfb6709d

+3 -3
+3 -3
net/netlink/af_netlink.c
··· 443 443 return 0; 444 444 445 445 netlink_remove(sk); 446 + sock_orphan(sk); 446 447 nlk = nlk_sk(sk); 447 448 448 449 spin_lock(&nlk->cb_lock); ··· 458 457 /* OK. Socket is unlinked, and, therefore, 459 458 no new packets will arrive */ 460 459 461 - sock_orphan(sk); 462 460 sock->sk = NULL; 463 461 wake_up_interruptible_all(&nlk->wait); 464 462 ··· 1412 1412 return -ECONNREFUSED; 1413 1413 } 1414 1414 nlk = nlk_sk(sk); 1415 - /* A dump is in progress... */ 1415 + /* A dump or destruction is in progress... */ 1416 1416 spin_lock(&nlk->cb_lock); 1417 - if (nlk->cb) { 1417 + if (nlk->cb || sock_flag(sk, SOCK_DEAD)) { 1418 1418 spin_unlock(&nlk->cb_lock); 1419 1419 netlink_destroy_callback(cb); 1420 1420 sock_put(sk);