]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tcp: refine tcp_sequence() for the FIN exception
authorEric Dumazet <edumazet@google.com>
Mon, 8 Jun 2026 15:14:52 +0000 (15:14 +0000)
committerJakub Kicinski <kuba@kernel.org>
Sat, 13 Jun 2026 20:40:37 +0000 (13:40 -0700)
Commit 0e24d17bd966 ("tcp: implement RFC 7323 window retraction
receiver requirements") removed the special FIN case that
was added in commit 1e3bb184e941 ("tcp: re-enable acceptance of
FIN packets when RWIN is 0").

If a peer sends a segment containing data and a FIN flag before
it learns about our window retraction and has a buggy TCP stack,
it might place the FIN one byte beyond what it thinks is the
right edge of the window (i.e., max_window_edge + 1).

The data portion (end_seq - th->fin) will end exactly at max_window_edge.
In this case, we will drop the packet if our receive queue is not empty,
even though the data was sent within the window we previously allowed.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Neal Cardwell <ncardwell@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Simon Baatz <gmbnomis@gmail.com>
Link: https://patch.msgid.link/20260608151452.706822-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/tcp_input.c

index ab7a4e5435a8a2cbb532d42c54af76d8541c903b..8560a9c6d38207c098d673497caf2c7652c36f5c 100644 (file)
@@ -4812,18 +4812,20 @@ static enum skb_drop_reason tcp_sequence(const struct sock *sk,
                                         const struct tcphdr *th)
 {
        const struct tcp_sock *tp = tcp_sk(sk);
+       u32 seq_limit;
 
        if (before(end_seq, tp->rcv_wup))
                return SKB_DROP_REASON_TCP_OLD_SEQUENCE;
 
-       if (unlikely(after(end_seq, tp->rcv_nxt + tcp_max_receive_window(tp)))) {
+       seq_limit = tp->rcv_nxt + tcp_max_receive_window(tp);
+       if (unlikely(after(end_seq, seq_limit))) {
                /* Some stacks are known to handle FIN incorrectly; allow the
                 * FIN to extend beyond the window and check it in detail later.
                 */
-               if (!after(end_seq - th->fin, tp->rcv_nxt + tcp_receive_window(tp)))
+               if (!after(end_seq - th->fin, seq_limit))
                        return SKB_NOT_DROPPED_YET;
 
-               if (after(seq, tp->rcv_nxt + tcp_max_receive_window(tp)))
+               if (after(seq, seq_limit))
                        return SKB_DROP_REASON_TCP_INVALID_SEQUENCE;
 
                /* Only accept this packet if receive queue is empty. */