Netdev List
 help / color / mirror / Atom feed
From: Jakub Kicinski <kuba@kernel.org>
To: michael.cohen3@mail.huji.ac.il
Cc: Jakub Kicinski <kuba@kernel.org>,
	netdev@vger.kernel.org, edumazet@google.com,
	ncardwell@google.com, kuniyu@google.com, davem@davemloft.net,
	pabeni@redhat.com, horms@kernel.org, tamir.shahar1@gmail.com,
	amit.klein@mail.huji.ac.il
Subject: Re: [PATCH net] tcp: reject completely old segments during sequence validation
Date: Thu, 20 Aug 2026 14:25:39 -0700	[thread overview]
Message-ID: <20260820212539.1180651-1-kuba@kernel.org> (raw)
In-Reply-To: <20260818205230.1146138-1-michael.cohen3@mail.huji.ac.il>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
tcp: reject completely old segments during sequence validation

tcp_sequence() previously only dropped a segment when end_seq was strictly
before rcv_wup.  This patch also drops the boundary case where end_seq
equals rcv_wup, as long as the segment consumes sequence space, so that
such segments no longer reach ACK processing.

> diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
> index 0f60a1dbf927..e51c76884460 100644
> --- a/net/ipv4/tcp_input.c
> +++ b/net/ipv4/tcp_input.c
> @@ -4844,7 +4844,8 @@ static enum skb_drop_reason tcp_sequence(const struct sock *sk,
>  	const struct tcp_sock *tp = tcp_sk(sk);
>  	u32 seq_limit;
>
> -	if (before(end_seq, tp->rcv_wup))
> +	if (before(end_seq, tp->rcv_wup) ||
> +	    (end_seq == tp->rcv_wup && seq != end_seq))
>  		return SKB_DROP_REASON_TCP_OLD_SEQUENCE;

[Severity: High]
Does this new clause also match the crossing SYN+ACK of a simultaneous
connect(), and does that re-introduce the behaviour fixed by commit
23e89e8ee7be7 ("tcp: Don't drop SYN+ACK for simultaneous connect.")?

When a TCP_SYN_SENT socket receives a bare SYN, the crossed-SYN branch in
tcp_rcv_synsent_state_process() moves it to TCP_SYN_RECV and leaves
rcv_wup equal to rcv_nxt:

net/ipv4/tcp_input.c:tcp_rcv_synsent_state_process() {
	...
	tcp_set_state(sk, TCP_SYN_RECV);
	...
	WRITE_ONCE(tp->rcv_nxt, TCP_SKB_CB(skb)->seq + 1);
	WRITE_ONCE(tp->copied_seq, tp->rcv_nxt);
	tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1;
	...
}

The following tcp_send_synack() sends a segment with TCPHDR_SYN set, and
__tcp_transmit_skb() only calls tcp_select_window() when TCPHDR_SYN is
clear, so rcv_wup is not advanced:

net/ipv4/tcp_output.c:__tcp_transmit_skb() {
	...
	if (likely(!(tcb->tcp_flags & TCPHDR_SYN))) {
		th->window	= htons(tcp_select_window(sk));
	...
}

The peer's bare SYN+ACK then arrives with end_seq == seq + 1, i.e.
end_seq == tp->rcv_nxt == tp->rcv_wup and seq != end_seq, which is exactly
what the new condition rejects with SKB_DROP_REASON_TCP_OLD_SEQUENCE.

Since th->syn is set, step 1 in tcp_validate_incoming() short-circuits
before step 4:

net/ipv4/tcp_input.c:tcp_validate_incoming() {
	...
	if (reason) {
		if (!th->rst) {
			if (th->syn)
				goto syn_challenge;
	...
}

Is the simultaneous-connect exception in step 4 still reachable after this
change?  It requires seq + 1 == end_seq and seq + 1 == tp->rcv_nxt, which
is the same condition the new step 1 check now filters out first:

net/ipv4/tcp_input.c:tcp_validate_incoming() {
	...
		if (sk->sk_state == TCP_SYN_RECV && sk->sk_socket && th->ack &&
		    TCP_SKB_CB(skb)->seq + 1 == TCP_SKB_CB(skb)->end_seq &&
		    TCP_SKB_CB(skb)->seq + 1 == tp->rcv_nxt &&
		    TCP_SKB_CB(skb)->ack_seq == tp->snd_nxt)
			goto pass;
	...
}

With that path taken, tcp_rcv_state_process() returns before tcp_ack() and
before the TCP_SYN_RECV to TCP_ESTABLISHED transition, so the visible
effects look like the ones described in 23e89e8ee7be7: a challenge ACK is
emitted instead, TCP_MIB_INERRS and LINUX_MIB_TCPSYNCHALLENGE are
incremented, establishment is delayed by an extra round trip until the
peer's pure ACK (seq == end_seq) is accepted, write() or send() right after
connect() returns -EAGAIN, and bpf_skops_parse_hdr() is not run for the
SYN+ACK.  Connect-to-self over loopback follows the same path.

Would restricting the new check so it does not apply while the socket is in
TCP_SYN_RECV with rcv_wup == rcv_nxt, or ordering it after the step 4
simultaneous-connect exception, preserve that case?

Cross-instance finding from sashiko-gemini (9561356177ca94322be4fdd8e89990a9b84fa51eecb45d5907ec165c8b9ca68e):
[Severity: Medium]
The patch breaks IPv6 flowlabel rehashing (ECMP repathing) on spurious RTO for non-SACK connections or when DSACK is disabled.

  reply	other threads:[~2026-08-20 21:25 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-18 20:52 [PATCH net] tcp: reject completely old segments during sequence validation Michael Cohen
2026-08-20 21:25 ` Jakub Kicinski [this message]
2026-08-20 21:26 ` Jakub Kicinski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260820212539.1180651-1-kuba@kernel.org \
    --to=kuba@kernel.org \
    --cc=amit.klein@mail.huji.ac.il \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kuniyu@google.com \
    --cc=michael.cohen3@mail.huji.ac.il \
    --cc=ncardwell@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=tamir.shahar1@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox