]> www.infradead.org Git - users/hch/misc.git/commitdiff
tcp: fix __tcp_close() to only send RST when required
authorEric Dumazet <edumazet@google.com>
Wed, 3 Sep 2025 08:47:18 +0000 (08:47 +0000)
committerJakub Kicinski <kuba@kernel.org>
Fri, 5 Sep 2025 02:13:41 +0000 (19:13 -0700)
If the receive queue contains payload that was already
received, __tcp_close() can send an unexpected RST.

Refine the code to take tp->copied_seq into account,
as we already do in tcp recvmsg().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Neal Cardwell <ncardwell@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Jason Xing <kerneljasonxing@gmail.com>
Link: https://patch.msgid.link/20250903084720.1168904-2-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/tcp.c

index 40b774b4f587a98f3519d7e00c463da61b1e3873..39eb03f6d07f4df209304a805f9a4e66cff0f2d9 100644 (file)
@@ -3099,8 +3099,8 @@ bool tcp_check_oom(const struct sock *sk, int shift)
 
 void __tcp_close(struct sock *sk, long timeout)
 {
+       bool data_was_unread = false;
        struct sk_buff *skb;
-       int data_was_unread = 0;
        int state;
 
        WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK);
@@ -3119,11 +3119,12 @@ void __tcp_close(struct sock *sk, long timeout)
         *  reader process may not have drained the data yet!
         */
        while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) {
-               u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq;
+               u32 end_seq = TCP_SKB_CB(skb)->end_seq;
 
                if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
-                       len--;
-               data_was_unread += len;
+                       end_seq--;
+               if (after(end_seq, tcp_sk(sk)->copied_seq))
+                       data_was_unread = true;
                __kfree_skb(skb);
        }