Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tcp: fix __tcp_close() to only send RST when required

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>

authored by

Eric Dumazet and committed by
Jakub Kicinski
5f923853 69777753

+5 -4
+5 -4
net/ipv4/tcp.c
··· 3099 3099 3100 3100 void __tcp_close(struct sock *sk, long timeout) 3101 3101 { 3102 + bool data_was_unread = false; 3102 3103 struct sk_buff *skb; 3103 - int data_was_unread = 0; 3104 3104 int state; 3105 3105 3106 3106 WRITE_ONCE(sk->sk_shutdown, SHUTDOWN_MASK); ··· 3119 3119 * reader process may not have drained the data yet! 3120 3120 */ 3121 3121 while ((skb = __skb_dequeue(&sk->sk_receive_queue)) != NULL) { 3122 - u32 len = TCP_SKB_CB(skb)->end_seq - TCP_SKB_CB(skb)->seq; 3122 + u32 end_seq = TCP_SKB_CB(skb)->end_seq; 3123 3123 3124 3124 if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) 3125 - len--; 3126 - data_was_unread += len; 3125 + end_seq--; 3126 + if (after(end_seq, tcp_sk(sk)->copied_seq)) 3127 + data_was_unread = true; 3127 3128 __kfree_skb(skb); 3128 3129 } 3129 3130