Skip to content

Commit 3f4ca5f

Browse files
JasonXingPaolo Abeni
authored andcommitted
tcp: avoid the lookup process failing to get sk in ehash table
While one cpu is working on looking up the right socket from ehash table, another cpu is done deleting the request socket and is about to add (or is adding) the big socket from the table. It means that we could miss both of them, even though it has little chance. Let me draw a call trace map of the server side. CPU 0 CPU 1 ----- ----- tcp_v4_rcv() syn_recv_sock() inet_ehash_insert() -> sk_nulls_del_node_init_rcu(osk) __inet_lookup_established() -> __sk_nulls_add_node_rcu(sk, list) Notice that the CPU 0 is receiving the data after the final ack during 3-way shakehands and CPU 1 is still handling the final ack. Why could this be a real problem? This case is happening only when the final ack and the first data receiving by different CPUs. Then the server receiving data with ACK flag tries to search one proper established socket from ehash table, but apparently it fails as my map shows above. After that, the server fetches a listener socket and then sends a RST because it finds a ACK flag in the skb (data), which obeys RST definition in RFC 793. Besides, Eric pointed out there's one more race condition where it handles tw socket hashdance. Only by adding to the tail of the list before deleting the old one can we avoid the race if the reader has already begun the bucket traversal and it would possibly miss the head. Many thanks to Eric for great help from beginning to end. Fixes: 5e0724d ("tcp/dccp: fix hashdance race for passive sessions") Suggested-by: Eric Dumazet <[email protected]> Signed-off-by: Jason Xing <[email protected]> Reviewed-by: Eric Dumazet <[email protected]> Reviewed-by: Kuniyuki Iwashima <[email protected]> Link: https://lore.kernel.org/lkml/[email protected]/ Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent 4fb58ac commit 3f4ca5f

File tree

2 files changed

+19
-6
lines changed

2 files changed

+19
-6
lines changed

net/ipv4/inet_hashtables.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -650,8 +650,20 @@ bool inet_ehash_insert(struct sock *sk, struct sock *osk, bool *found_dup_sk)
650650
spin_lock(lock);
651651
if (osk) {
652652
WARN_ON_ONCE(sk->sk_hash != osk->sk_hash);
653-
ret = sk_nulls_del_node_init_rcu(osk);
654-
} else if (found_dup_sk) {
653+
ret = sk_hashed(osk);
654+
if (ret) {
655+
/* Before deleting the node, we insert a new one to make
656+
* sure that the look-up-sk process would not miss either
657+
* of them and that at least one node would exist in ehash
658+
* table all the time. Otherwise there's a tiny chance
659+
* that lookup process could find nothing in ehash table.
660+
*/
661+
__sk_nulls_add_node_tail_rcu(sk, list);
662+
sk_nulls_del_node_init_rcu(osk);
663+
}
664+
goto unlock;
665+
}
666+
if (found_dup_sk) {
655667
*found_dup_sk = inet_ehash_lookup_by_sk(sk, list);
656668
if (*found_dup_sk)
657669
ret = false;
@@ -660,6 +672,7 @@ bool inet_ehash_insert(struct sock *sk, struct sock *osk, bool *found_dup_sk)
660672
if (ret)
661673
__sk_nulls_add_node_rcu(sk, list);
662674

675+
unlock:
663676
spin_unlock(lock);
664677

665678
return ret;

net/ipv4/inet_timewait_sock.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ void inet_twsk_put(struct inet_timewait_sock *tw)
9191
}
9292
EXPORT_SYMBOL_GPL(inet_twsk_put);
9393

94-
static void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw,
95-
struct hlist_nulls_head *list)
94+
static void inet_twsk_add_node_tail_rcu(struct inet_timewait_sock *tw,
95+
struct hlist_nulls_head *list)
9696
{
97-
hlist_nulls_add_head_rcu(&tw->tw_node, list);
97+
hlist_nulls_add_tail_rcu(&tw->tw_node, list);
9898
}
9999

100100
static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
@@ -147,7 +147,7 @@ void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
147147

148148
spin_lock(lock);
149149

150-
inet_twsk_add_node_rcu(tw, &ehead->chain);
150+
inet_twsk_add_node_tail_rcu(tw, &ehead->chain);
151151

152152
/* Step 3: Remove SK from hash chain */
153153
if (__sk_nulls_del_node_init_rcu(sk))

0 commit comments

Comments
 (0)