net/ipv4/tcp_input.c | 2 ++ 1 file changed, 2 insertions(+)
When a TCP connection is in FIN-WAIT-1 state with the FIN packet blocked in
the send buffer, and the peer continuously sends zero-window advertisements,
the current implementation reset the zero-window probe timer while maintaining
the current `icsk->icsk_backoff`, causing the connection to remain permanently
in FIN-WAIT-1 state.
Reproduce conditions:
1. Peer's receive window is full and actively sending continuous zero window
advertisements.
2. Local FIN packet is blocked in send buffer due to peer's zero-window.
3. Local socket has been closed (entered orphan state).
The root cause lies in the tcp_ack_probe() function: when receiving a zero-window ACK,
- It reset the probe timer while keeping the current `icsk->icsk_backoff`.
- This would result in the condition `icsk->icsk_backoff >= max_probes` false.
- Orphaned socket cannot be set to close.
This patch modifies the tcp_ack_probe() logic: when the socket is dead,
upon receiving a zero-window packet, instead of resetting the probe timer,
we maintain the current timer, ensuring the probe interval grows according
to 'icsk->icsk_backoff', thus causing the zero-window probe timer to eventually
timeout and close the socket.
Signed-off-by: HaiYang Zhong <wokezhong@tencent.com>
---
net/ipv4/tcp_input.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 71b76e98371a..22fc82cb6b73 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -3440,6 +3440,8 @@ static void tcp_ack_probe(struct sock *sk)
} else {
unsigned long when = tcp_probe0_when(sk, tcp_rto_max(sk));
+ if (sock_flag(sk, SOCK_DEAD) && icsk->icsk_backoff != 0)
+ return;
when = tcp_clamp_probe0_to_user_timeout(sk, when);
tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, when, true);
}
--
2.43.7
On Fri, Sep 19, 2025 at 10:30 AM HaiYang Zhong <wokezhong@gmail.com> wrote: > > When a TCP connection is in FIN-WAIT-1 state with the FIN packet blocked in > the send buffer, and the peer continuously sends zero-window advertisements, > the current implementation reset the zero-window probe timer while maintaining > the current `icsk->icsk_backoff`, causing the connection to remain permanently > in FIN-WAIT-1 state. > > Reproduce conditions: > 1. Peer's receive window is full and actively sending continuous zero window > advertisements. > 2. Local FIN packet is blocked in send buffer due to peer's zero-window. > 3. Local socket has been closed (entered orphan state). > > The root cause lies in the tcp_ack_probe() function: when receiving a zero-window ACK, > - It reset the probe timer while keeping the current `icsk->icsk_backoff`. > - This would result in the condition `icsk->icsk_backoff >= max_probes` false. > - Orphaned socket cannot be set to close. > > This patch modifies the tcp_ack_probe() logic: when the socket is dead, > upon receiving a zero-window packet, instead of resetting the probe timer, > we maintain the current timer, ensuring the probe interval grows according > to 'icsk->icsk_backoff', thus causing the zero-window probe timer to eventually > timeout and close the socket. > > Signed-off-by: HaiYang Zhong <wokezhong@tencent.com> > --- > net/ipv4/tcp_input.c | 2 ++ > 1 file changed, 2 insertions(+) > > diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c > index 71b76e98371a..22fc82cb6b73 100644 > --- a/net/ipv4/tcp_input.c > +++ b/net/ipv4/tcp_input.c > @@ -3440,6 +3440,8 @@ static void tcp_ack_probe(struct sock *sk) > } else { > unsigned long when = tcp_probe0_when(sk, tcp_rto_max(sk)); > > + if (sock_flag(sk, SOCK_DEAD) && icsk->icsk_backoff != 0) > + return; > when = tcp_clamp_probe0_to_user_timeout(sk, when); > tcp_reset_xmit_timer(sk, ICSK_TIME_PROBE0, when, true); > } > -- > 2.43.7 Hi there. Seems reasonable, but could you provide a packetdrill test ? Also, what if the FIN was already sent, but the peer retracted its RWIN ? tcp_ack_probe() would return early (if (!head) return;)
© 2016 - 2025 Red Hat, Inc.