]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
tcp: do not accept packets beyond window
authorEric Dumazet <edumazet@google.com>
Fri, 11 Jul 2025 11:39:59 +0000 (11:39 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 15 Jul 2025 01:41:15 +0000 (18:41 -0700)
Currently, TCP accepts incoming packets which might go beyond the
offered RWIN.

Add to tcp_sequence() the validation of packet end sequence.

Add the corresponding check in the fast path.

We relax this new constraint if the receive queue is empty,
to not freeze flows from buggy peers.

Add a new drop reason : SKB_DROP_REASON_TCP_INVALID_END_SEQUENCE.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250711114006.480026-2-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
include/net/dropreason-core.h
net/ipv4/tcp_input.c

index b9e78290269e6e7d9d9155171f6b0ef03c7697c9..beb134d5574795d00436678bf056e2bfc12389a9 100644 (file)
@@ -45,6 +45,7 @@
        FN(TCP_LISTEN_OVERFLOW)         \
        FN(TCP_OLD_SEQUENCE)            \
        FN(TCP_INVALID_SEQUENCE)        \
+       FN(TCP_INVALID_END_SEQUENCE)    \
        FN(TCP_INVALID_ACK_SEQUENCE)    \
        FN(TCP_RESET)                   \
        FN(TCP_INVALID_SYN)             \
@@ -303,8 +304,13 @@ enum skb_drop_reason {
        SKB_DROP_REASON_TCP_LISTEN_OVERFLOW,
        /** @SKB_DROP_REASON_TCP_OLD_SEQUENCE: Old SEQ field (duplicate packet) */
        SKB_DROP_REASON_TCP_OLD_SEQUENCE,
-       /** @SKB_DROP_REASON_TCP_INVALID_SEQUENCE: Not acceptable SEQ field */
+       /** @SKB_DROP_REASON_TCP_INVALID_SEQUENCE: Not acceptable SEQ field. */
        SKB_DROP_REASON_TCP_INVALID_SEQUENCE,
+       /**
+        * @SKB_DROP_REASON_TCP_INVALID_END_SEQUENCE:
+        * Not acceptable END_SEQ field.
+        */
+       SKB_DROP_REASON_TCP_INVALID_END_SEQUENCE,
        /**
         * @SKB_DROP_REASON_TCP_INVALID_ACK_SEQUENCE: Not acceptable ACK SEQ
         * field because ack sequence is not in the window between snd_una
index 9b03c44c12b862b5d33f4390cfc85e2f8897cd8e..f0f9c78654b449cb2a122e8c53fdcc96e5317de7 100644 (file)
@@ -4391,14 +4391,22 @@ static enum skb_drop_reason tcp_disordered_ack_check(const struct sock *sk,
  * (borrowed from freebsd)
  */
 
-static enum skb_drop_reason tcp_sequence(const struct tcp_sock *tp,
+static enum skb_drop_reason tcp_sequence(const struct sock *sk,
                                         u32 seq, u32 end_seq)
 {
+       const struct tcp_sock *tp = tcp_sk(sk);
+
        if (before(end_seq, tp->rcv_wup))
                return SKB_DROP_REASON_TCP_OLD_SEQUENCE;
 
-       if (after(seq, tp->rcv_nxt + tcp_receive_window(tp)))
-               return SKB_DROP_REASON_TCP_INVALID_SEQUENCE;
+       if (after(end_seq, tp->rcv_nxt + tcp_receive_window(tp))) {
+               if (after(seq, tp->rcv_nxt + tcp_receive_window(tp)))
+                       return SKB_DROP_REASON_TCP_INVALID_SEQUENCE;
+
+               /* Only accept this packet if receive queue is empty. */
+               if (skb_queue_len(&sk->sk_receive_queue))
+                       return SKB_DROP_REASON_TCP_INVALID_END_SEQUENCE;
+       }
 
        return SKB_NOT_DROPPED_YET;
 }
@@ -5881,7 +5889,7 @@ static bool tcp_validate_incoming(struct sock *sk, struct sk_buff *skb,
 
 step1:
        /* Step 1: check sequence number */
-       reason = tcp_sequence(tp, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
+       reason = tcp_sequence(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
        if (reason) {
                /* RFC793, page 37: "In all states except SYN-SENT, all reset
                 * (RST) segments are validated by checking their SEQ-fields."
@@ -6110,6 +6118,10 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb)
                        if (tcp_checksum_complete(skb))
                                goto csum_error;
 
+                       if (after(TCP_SKB_CB(skb)->end_seq,
+                                 tp->rcv_nxt + tcp_receive_window(tp)))
+                               goto validate;
+
                        if ((int)skb->truesize > sk->sk_forward_alloc)
                                goto step5;
 
@@ -6165,7 +6177,7 @@ slow_path:
        /*
         *      Standard slow path.
         */
-
+validate:
        if (!tcp_validate_incoming(sk, skb, th, 1))
                return;