diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2015-09-18 07:16:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-09-21 01:55:31 -0400 |
commit | 1f770c0a09da855a2b51af6d19de97fb955eca85 (patch) | |
tree | eeb43c3ce6917390ce6e55b48990d2ba32aaf9cc /net/netlink/af_netlink.c | |
parent | 3ea79249e81e5ed051f2e6480cbde896d99046e8 (diff) |
netlink: Fix autobind race condition that leads to zero port ID
The commit c0bb07df7d981e4091432754e30c9c720e2c0c78 ("netlink:
Reset portid after netlink_insert failure") introduced a race
condition where if two threads try to autobind the same socket
one of them may end up with a zero port ID. This led to kernel
deadlocks that were observed by multiple people.
This patch reverts that commit and instead fixes it by introducing
a separte rhash_portid variable so that the real portid is only set
after the socket has been successfully hashed.
Fixes: c0bb07df7d98 ("netlink: Reset portid after netlink_insert failure")
Reported-by: Tejun Heo <tj@kernel.org>
Reported-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink/af_netlink.c')
-rw-r--r-- | net/netlink/af_netlink.c | 12 |
1 files changed, 7 insertions, 5 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 4cad99d6c68b..9f51608b968a 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -1031,7 +1031,7 @@ static inline int netlink_compare(struct rhashtable_compare_arg *arg, | |||
1031 | const struct netlink_compare_arg *x = arg->key; | 1031 | const struct netlink_compare_arg *x = arg->key; |
1032 | const struct netlink_sock *nlk = ptr; | 1032 | const struct netlink_sock *nlk = ptr; |
1033 | 1033 | ||
1034 | return nlk->portid != x->portid || | 1034 | return nlk->rhash_portid != x->portid || |
1035 | !net_eq(sock_net(&nlk->sk), read_pnet(&x->pnet)); | 1035 | !net_eq(sock_net(&nlk->sk), read_pnet(&x->pnet)); |
1036 | } | 1036 | } |
1037 | 1037 | ||
@@ -1057,7 +1057,7 @@ static int __netlink_insert(struct netlink_table *table, struct sock *sk) | |||
1057 | { | 1057 | { |
1058 | struct netlink_compare_arg arg; | 1058 | struct netlink_compare_arg arg; |
1059 | 1059 | ||
1060 | netlink_compare_arg_init(&arg, sock_net(sk), nlk_sk(sk)->portid); | 1060 | netlink_compare_arg_init(&arg, sock_net(sk), nlk_sk(sk)->rhash_portid); |
1061 | return rhashtable_lookup_insert_key(&table->hash, &arg, | 1061 | return rhashtable_lookup_insert_key(&table->hash, &arg, |
1062 | &nlk_sk(sk)->node, | 1062 | &nlk_sk(sk)->node, |
1063 | netlink_rhashtable_params); | 1063 | netlink_rhashtable_params); |
@@ -1119,7 +1119,7 @@ static int netlink_insert(struct sock *sk, u32 portid) | |||
1119 | unlikely(atomic_read(&table->hash.nelems) >= UINT_MAX)) | 1119 | unlikely(atomic_read(&table->hash.nelems) >= UINT_MAX)) |
1120 | goto err; | 1120 | goto err; |
1121 | 1121 | ||
1122 | nlk_sk(sk)->portid = portid; | 1122 | nlk_sk(sk)->rhash_portid = portid; |
1123 | sock_hold(sk); | 1123 | sock_hold(sk); |
1124 | 1124 | ||
1125 | err = __netlink_insert(table, sk); | 1125 | err = __netlink_insert(table, sk); |
@@ -1131,10 +1131,12 @@ static int netlink_insert(struct sock *sk, u32 portid) | |||
1131 | err = -EOVERFLOW; | 1131 | err = -EOVERFLOW; |
1132 | if (err == -EEXIST) | 1132 | if (err == -EEXIST) |
1133 | err = -EADDRINUSE; | 1133 | err = -EADDRINUSE; |
1134 | nlk_sk(sk)->portid = 0; | ||
1135 | sock_put(sk); | 1134 | sock_put(sk); |
1135 | goto err; | ||
1136 | } | 1136 | } |
1137 | 1137 | ||
1138 | nlk_sk(sk)->portid = portid; | ||
1139 | |||
1138 | err: | 1140 | err: |
1139 | release_sock(sk); | 1141 | release_sock(sk); |
1140 | return err; | 1142 | return err; |
@@ -3271,7 +3273,7 @@ static inline u32 netlink_hash(const void *data, u32 len, u32 seed) | |||
3271 | const struct netlink_sock *nlk = data; | 3273 | const struct netlink_sock *nlk = data; |
3272 | struct netlink_compare_arg arg; | 3274 | struct netlink_compare_arg arg; |
3273 | 3275 | ||
3274 | netlink_compare_arg_init(&arg, sock_net(&nlk->sk), nlk->portid); | 3276 | netlink_compare_arg_init(&arg, sock_net(&nlk->sk), nlk->rhash_portid); |
3275 | return jhash2((u32 *)&arg, netlink_compare_arg_len / sizeof(u32), seed); | 3277 | return jhash2((u32 *)&arg, netlink_compare_arg_len / sizeof(u32), seed); |
3276 | } | 3278 | } |
3277 | 3279 | ||