Commit 9ca48d61 authored by Eric Dumazet's avatar Eric Dumazet Committed by Jakub Kicinski
Browse files

tcp: do not accept packets beyond window



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: default avatarEric Dumazet <edumazet@google.com>
Reviewed-by: default avatarKuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250711114006.480026-2-edumazet@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent a86eb2a6
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -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
+17 −5
Original line number Diff line number Diff line
@@ -4391,15 +4391,23 @@ 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(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 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb)
	/*
	 *	Standard slow path.
	 */

validate:
	if (!tcp_validate_incoming(sk, skb, th, 1))
		return;