Patch "tcp: avoid the lookup process failing to get sk in ehash table" has been added to the 5.10-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    tcp: avoid the lookup process failing to get sk in ehash table

to the 5.10-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     tcp-avoid-the-lookup-process-failing-to-get-sk-in-eh.patch
and it can be found in the queue-5.10 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit ba9b39bdaae3c46edd96dfc6bdbd7f4ac43cf43e
Author: Jason Xing <kernelxing@xxxxxxxxxxx>
Date:   Wed Jan 18 09:59:41 2023 +0800

    tcp: avoid the lookup process failing to get sk in ehash table
    
    [ Upstream commit 3f4ca5fafc08881d7a57daa20449d171f2887043 ]
    
    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: 5e0724d027f0 ("tcp/dccp: fix hashdance race for passive sessions")
    Suggested-by: Eric Dumazet <edumazet@xxxxxxxxxx>
    Signed-off-by: Jason Xing <kernelxing@xxxxxxxxxxx>
    Reviewed-by: Eric Dumazet <edumazet@xxxxxxxxxx>
    Reviewed-by: Kuniyuki Iwashima <kuniyu@xxxxxxxxxx>
    Link: https://lore.kernel.org/lkml/20230112065336.41034-1-kerneljasonxing@xxxxxxxxx/
    Link: https://lore.kernel.org/r/20230118015941.1313-1-kerneljasonxing@xxxxxxxxx
    Signed-off-by: Paolo Abeni <pabeni@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index c68a1dae25ca..2615b72118d1 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -571,8 +571,20 @@ bool inet_ehash_insert(struct sock *sk, struct sock *osk, bool *found_dup_sk)
 	spin_lock(lock);
 	if (osk) {
 		WARN_ON_ONCE(sk->sk_hash != osk->sk_hash);
-		ret = sk_nulls_del_node_init_rcu(osk);
-	} else if (found_dup_sk) {
+		ret = sk_hashed(osk);
+		if (ret) {
+			/* Before deleting the node, we insert a new one to make
+			 * sure that the look-up-sk process would not miss either
+			 * of them and that at least one node would exist in ehash
+			 * table all the time. Otherwise there's a tiny chance
+			 * that lookup process could find nothing in ehash table.
+			 */
+			__sk_nulls_add_node_tail_rcu(sk, list);
+			sk_nulls_del_node_init_rcu(osk);
+		}
+		goto unlock;
+	}
+	if (found_dup_sk) {
 		*found_dup_sk = inet_ehash_lookup_by_sk(sk, list);
 		if (*found_dup_sk)
 			ret = false;
@@ -581,6 +593,7 @@ bool inet_ehash_insert(struct sock *sk, struct sock *osk, bool *found_dup_sk)
 	if (ret)
 		__sk_nulls_add_node_rcu(sk, list);
 
+unlock:
 	spin_unlock(lock);
 
 	return ret;
diff --git a/net/ipv4/inet_timewait_sock.c b/net/ipv4/inet_timewait_sock.c
index c411c87ae865..a00102d7c7fd 100644
--- a/net/ipv4/inet_timewait_sock.c
+++ b/net/ipv4/inet_timewait_sock.c
@@ -81,10 +81,10 @@ void inet_twsk_put(struct inet_timewait_sock *tw)
 }
 EXPORT_SYMBOL_GPL(inet_twsk_put);
 
-static void inet_twsk_add_node_rcu(struct inet_timewait_sock *tw,
-				   struct hlist_nulls_head *list)
+static void inet_twsk_add_node_tail_rcu(struct inet_timewait_sock *tw,
+					struct hlist_nulls_head *list)
 {
-	hlist_nulls_add_head_rcu(&tw->tw_node, list);
+	hlist_nulls_add_tail_rcu(&tw->tw_node, list);
 }
 
 static void inet_twsk_add_bind_node(struct inet_timewait_sock *tw,
@@ -120,7 +120,7 @@ void inet_twsk_hashdance(struct inet_timewait_sock *tw, struct sock *sk,
 
 	spin_lock(lock);
 
-	inet_twsk_add_node_rcu(tw, &ehead->chain);
+	inet_twsk_add_node_tail_rcu(tw, &ehead->chain);
 
 	/* Step 3: Remove SK from hash chain */
 	if (__sk_nulls_del_node_init_rcu(sk))



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux