Commit 4e27762417669cb459971635be550eb7b5598286
Committed by
Greg Kroah-Hartman
1 parent
d600176461
Exists in
ti-lsk-linux-4.1.y
and in
5 other branches
netlink: Fix autobind race condition that leads to zero port ID
[ Upstream commit 1f770c0a09da855a2b51af6d19de97fb955eca85 ] 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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 2 changed files with 7 additions and 5 deletions Side-by-side Diff
net/netlink/af_netlink.c
... | ... | @@ -1017,7 +1017,7 @@ |
1017 | 1017 | const struct netlink_compare_arg *x = arg->key; |
1018 | 1018 | const struct netlink_sock *nlk = ptr; |
1019 | 1019 | |
1020 | - return nlk->portid != x->portid || | |
1020 | + return nlk->rhash_portid != x->portid || | |
1021 | 1021 | !net_eq(sock_net(&nlk->sk), read_pnet(&x->pnet)); |
1022 | 1022 | } |
1023 | 1023 | |
... | ... | @@ -1043,7 +1043,7 @@ |
1043 | 1043 | { |
1044 | 1044 | struct netlink_compare_arg arg; |
1045 | 1045 | |
1046 | - netlink_compare_arg_init(&arg, sock_net(sk), nlk_sk(sk)->portid); | |
1046 | + netlink_compare_arg_init(&arg, sock_net(sk), nlk_sk(sk)->rhash_portid); | |
1047 | 1047 | return rhashtable_lookup_insert_key(&table->hash, &arg, |
1048 | 1048 | &nlk_sk(sk)->node, |
1049 | 1049 | netlink_rhashtable_params); |
... | ... | @@ -1105,7 +1105,7 @@ |
1105 | 1105 | unlikely(atomic_read(&table->hash.nelems) >= UINT_MAX)) |
1106 | 1106 | goto err; |
1107 | 1107 | |
1108 | - nlk_sk(sk)->portid = portid; | |
1108 | + nlk_sk(sk)->rhash_portid = portid; | |
1109 | 1109 | sock_hold(sk); |
1110 | 1110 | |
1111 | 1111 | err = __netlink_insert(table, sk); |
1112 | 1112 | |
... | ... | @@ -1117,10 +1117,11 @@ |
1117 | 1117 | err = -EOVERFLOW; |
1118 | 1118 | if (err == -EEXIST) |
1119 | 1119 | err = -EADDRINUSE; |
1120 | - nlk_sk(sk)->portid = 0; | |
1121 | 1120 | sock_put(sk); |
1122 | 1121 | } |
1123 | 1122 | |
1123 | + nlk_sk(sk)->portid = portid; | |
1124 | + | |
1124 | 1125 | err: |
1125 | 1126 | release_sock(sk); |
1126 | 1127 | return err; |
... | ... | @@ -3167,7 +3168,7 @@ |
3167 | 3168 | const struct netlink_sock *nlk = data; |
3168 | 3169 | struct netlink_compare_arg arg; |
3169 | 3170 | |
3170 | - netlink_compare_arg_init(&arg, sock_net(&nlk->sk), nlk->portid); | |
3171 | + netlink_compare_arg_init(&arg, sock_net(&nlk->sk), nlk->rhash_portid); | |
3171 | 3172 | return jhash2((u32 *)&arg, netlink_compare_arg_len / sizeof(u32), seed); |
3172 | 3173 | } |
3173 | 3174 |