diff options
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r-- | net/netlink/af_netlink.c | 48 |
1 files changed, 33 insertions, 15 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 074cf3e91c6f..02fdde28dada 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -61,6 +61,7 @@ | |||
61 | #include <linux/rhashtable.h> | 61 | #include <linux/rhashtable.h> |
62 | #include <asm/cacheflush.h> | 62 | #include <asm/cacheflush.h> |
63 | #include <linux/hash.h> | 63 | #include <linux/hash.h> |
64 | #include <linux/genetlink.h> | ||
64 | 65 | ||
65 | #include <net/net_namespace.h> | 66 | #include <net/net_namespace.h> |
66 | #include <net/sock.h> | 67 | #include <net/sock.h> |
@@ -1091,8 +1092,12 @@ static void netlink_remove(struct sock *sk) | |||
1091 | mutex_unlock(&nl_sk_hash_lock); | 1092 | mutex_unlock(&nl_sk_hash_lock); |
1092 | 1093 | ||
1093 | netlink_table_grab(); | 1094 | netlink_table_grab(); |
1094 | if (nlk_sk(sk)->subscriptions) | 1095 | if (nlk_sk(sk)->subscriptions) { |
1095 | __sk_del_bind_node(sk); | 1096 | __sk_del_bind_node(sk); |
1097 | netlink_update_listeners(sk); | ||
1098 | } | ||
1099 | if (sk->sk_protocol == NETLINK_GENERIC) | ||
1100 | atomic_inc(&genl_sk_destructing_cnt); | ||
1096 | netlink_table_ungrab(); | 1101 | netlink_table_ungrab(); |
1097 | } | 1102 | } |
1098 | 1103 | ||
@@ -1139,8 +1144,8 @@ static int netlink_create(struct net *net, struct socket *sock, int protocol, | |||
1139 | struct module *module = NULL; | 1144 | struct module *module = NULL; |
1140 | struct mutex *cb_mutex; | 1145 | struct mutex *cb_mutex; |
1141 | struct netlink_sock *nlk; | 1146 | struct netlink_sock *nlk; |
1142 | int (*bind)(int group); | 1147 | int (*bind)(struct net *net, int group); |
1143 | void (*unbind)(int group); | 1148 | void (*unbind)(struct net *net, int group); |
1144 | int err = 0; | 1149 | int err = 0; |
1145 | 1150 | ||
1146 | sock->state = SS_UNCONNECTED; | 1151 | sock->state = SS_UNCONNECTED; |
@@ -1209,6 +1214,20 @@ static int netlink_release(struct socket *sock) | |||
1209 | * will be purged. | 1214 | * will be purged. |
1210 | */ | 1215 | */ |
1211 | 1216 | ||
1217 | /* must not acquire netlink_table_lock in any way again before unbind | ||
1218 | * and notifying genetlink is done as otherwise it might deadlock | ||
1219 | */ | ||
1220 | if (nlk->netlink_unbind) { | ||
1221 | int i; | ||
1222 | |||
1223 | for (i = 0; i < nlk->ngroups; i++) | ||
1224 | if (test_bit(i, nlk->groups)) | ||
1225 | nlk->netlink_unbind(sock_net(sk), i + 1); | ||
1226 | } | ||
1227 | if (sk->sk_protocol == NETLINK_GENERIC && | ||
1228 | atomic_dec_return(&genl_sk_destructing_cnt) == 0) | ||
1229 | wake_up(&genl_sk_destructing_waitq); | ||
1230 | |||
1212 | sock->sk = NULL; | 1231 | sock->sk = NULL; |
1213 | wake_up_interruptible_all(&nlk->wait); | 1232 | wake_up_interruptible_all(&nlk->wait); |
1214 | 1233 | ||
@@ -1226,8 +1245,8 @@ static int netlink_release(struct socket *sock) | |||
1226 | 1245 | ||
1227 | module_put(nlk->module); | 1246 | module_put(nlk->module); |
1228 | 1247 | ||
1229 | netlink_table_grab(); | ||
1230 | if (netlink_is_kernel(sk)) { | 1248 | if (netlink_is_kernel(sk)) { |
1249 | netlink_table_grab(); | ||
1231 | BUG_ON(nl_table[sk->sk_protocol].registered == 0); | 1250 | BUG_ON(nl_table[sk->sk_protocol].registered == 0); |
1232 | if (--nl_table[sk->sk_protocol].registered == 0) { | 1251 | if (--nl_table[sk->sk_protocol].registered == 0) { |
1233 | struct listeners *old; | 1252 | struct listeners *old; |
@@ -1241,10 +1260,8 @@ static int netlink_release(struct socket *sock) | |||
1241 | nl_table[sk->sk_protocol].flags = 0; | 1260 | nl_table[sk->sk_protocol].flags = 0; |
1242 | nl_table[sk->sk_protocol].registered = 0; | 1261 | nl_table[sk->sk_protocol].registered = 0; |
1243 | } | 1262 | } |
1244 | } else if (nlk->subscriptions) { | 1263 | netlink_table_ungrab(); |
1245 | netlink_update_listeners(sk); | ||
1246 | } | 1264 | } |
1247 | netlink_table_ungrab(); | ||
1248 | 1265 | ||
1249 | kfree(nlk->groups); | 1266 | kfree(nlk->groups); |
1250 | nlk->groups = NULL; | 1267 | nlk->groups = NULL; |
@@ -1410,9 +1427,10 @@ static int netlink_realloc_groups(struct sock *sk) | |||
1410 | return err; | 1427 | return err; |
1411 | } | 1428 | } |
1412 | 1429 | ||
1413 | static void netlink_unbind(int group, long unsigned int groups, | 1430 | static void netlink_undo_bind(int group, long unsigned int groups, |
1414 | struct netlink_sock *nlk) | 1431 | struct sock *sk) |
1415 | { | 1432 | { |
1433 | struct netlink_sock *nlk = nlk_sk(sk); | ||
1416 | int undo; | 1434 | int undo; |
1417 | 1435 | ||
1418 | if (!nlk->netlink_unbind) | 1436 | if (!nlk->netlink_unbind) |
@@ -1420,7 +1438,7 @@ static void netlink_unbind(int group, long unsigned int groups, | |||
1420 | 1438 | ||
1421 | for (undo = 0; undo < group; undo++) | 1439 | for (undo = 0; undo < group; undo++) |
1422 | if (test_bit(undo, &groups)) | 1440 | if (test_bit(undo, &groups)) |
1423 | nlk->netlink_unbind(undo); | 1441 | nlk->netlink_unbind(sock_net(sk), undo); |
1424 | } | 1442 | } |
1425 | 1443 | ||
1426 | static int netlink_bind(struct socket *sock, struct sockaddr *addr, | 1444 | static int netlink_bind(struct socket *sock, struct sockaddr *addr, |
@@ -1458,10 +1476,10 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, | |||
1458 | for (group = 0; group < nlk->ngroups; group++) { | 1476 | for (group = 0; group < nlk->ngroups; group++) { |
1459 | if (!test_bit(group, &groups)) | 1477 | if (!test_bit(group, &groups)) |
1460 | continue; | 1478 | continue; |
1461 | err = nlk->netlink_bind(group); | 1479 | err = nlk->netlink_bind(net, group); |
1462 | if (!err) | 1480 | if (!err) |
1463 | continue; | 1481 | continue; |
1464 | netlink_unbind(group, groups, nlk); | 1482 | netlink_undo_bind(group, groups, sk); |
1465 | return err; | 1483 | return err; |
1466 | } | 1484 | } |
1467 | } | 1485 | } |
@@ -1471,7 +1489,7 @@ static int netlink_bind(struct socket *sock, struct sockaddr *addr, | |||
1471 | netlink_insert(sk, net, nladdr->nl_pid) : | 1489 | netlink_insert(sk, net, nladdr->nl_pid) : |
1472 | netlink_autobind(sock); | 1490 | netlink_autobind(sock); |
1473 | if (err) { | 1491 | if (err) { |
1474 | netlink_unbind(nlk->ngroups, groups, nlk); | 1492 | netlink_undo_bind(nlk->ngroups, groups, sk); |
1475 | return err; | 1493 | return err; |
1476 | } | 1494 | } |
1477 | } | 1495 | } |
@@ -2122,7 +2140,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
2122 | if (!val || val - 1 >= nlk->ngroups) | 2140 | if (!val || val - 1 >= nlk->ngroups) |
2123 | return -EINVAL; | 2141 | return -EINVAL; |
2124 | if (optname == NETLINK_ADD_MEMBERSHIP && nlk->netlink_bind) { | 2142 | if (optname == NETLINK_ADD_MEMBERSHIP && nlk->netlink_bind) { |
2125 | err = nlk->netlink_bind(val); | 2143 | err = nlk->netlink_bind(sock_net(sk), val); |
2126 | if (err) | 2144 | if (err) |
2127 | return err; | 2145 | return err; |
2128 | } | 2146 | } |
@@ -2131,7 +2149,7 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, | |||
2131 | optname == NETLINK_ADD_MEMBERSHIP); | 2149 | optname == NETLINK_ADD_MEMBERSHIP); |
2132 | netlink_table_ungrab(); | 2150 | netlink_table_ungrab(); |
2133 | if (optname == NETLINK_DROP_MEMBERSHIP && nlk->netlink_unbind) | 2151 | if (optname == NETLINK_DROP_MEMBERSHIP && nlk->netlink_unbind) |
2134 | nlk->netlink_unbind(val); | 2152 | nlk->netlink_unbind(sock_net(sk), val); |
2135 | 2153 | ||
2136 | err = 0; | 2154 | err = 0; |
2137 | break; | 2155 | break; |