git: c9df1a6cf9be - main - tcp: improve SEG.SEQ validation for RST segments

Michael Tuexen <[email protected]> Mon, 03 Aug 2026 11:16:18 +0000
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <6a707882.188a3.505d6563__5037.2992687457$1785755817$gmane$org@gitrepo.freebsd.org>
The branch main has been updated by tuexen:

URL: https://cgit.FreeBSD.org/src/commit/?id=c9df1a6cf9be9d44eacc8616ebba1cd19010c7fc

commit c9df1a6cf9be9d44eacc8616ebba1cd19010c7fc
Author:     Michael Tuexen <[email protected]>
AuthorDate: 2026-08-03 11:07:32 +0000
Commit:     Michael Tuexen <[email protected]>
CommitDate: 2026-08-03 11:07:32 +0000

    tcp: improve SEG.SEQ validation for RST segments
    
    A RST segment can be sent in response to
    (a) received segment or
    (b) by the upper layer protocol.
    
    The SEG.SEQ validation consists of two checks:
    (1) the in-window check of SEG.SEQ and
    (2) the exact match check of SEG.SEQ.
    
    For the in-window check (1), the left edge of the window needs to be
    based on tp->last_ack_sent to cover the delayed ACK case, whereas the
    right edge needs to be based on tp->rcv_nxt + tp->rcv_wnd. This both
    assumes that tp->rcv_wnd is not zero. For the special case of
    tp->rcv_wnd being zero, add checks against tp->last_ack_sent for (a)
    and on tp->rcv_nxt for (b). This applies to all TCP stacks.
    
    When the exact match (2) of SEG.SEQ is performed, it should be based
    on tp->last_ack_sent for (a) and on tp->rcv_nxt for (b). To cover both,
    check for both. Add this only to the base stack, since the RACK and
    BBR stacks already do this.
    
    PR:                     296594
    Reviewed by:            rscheff
    MFC after:              3 days
    MFC to:                 stable/14
    MFC to:                 stable/15
    Sponsored by:           Netflix, Inc.
    Differential Revision:  https://reviews.freebsd.org/D58594
---
 sys/netinet/tcp_input.c                  | 18 ++++++++++++------
 sys/netinet/tcp_stacks/rack_bbr_common.c | 22 ++++++++++++++--------
 2 files changed, 26 insertions(+), 14 deletions(-)

diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index bfef374af2d9..1e60774b3fb0 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -2125,20 +2125,26 @@ tcp_do_segment(struct tcpcb *tp, struct mbuf *m, struct tcphdr *th,
 		 * - RST drops connection only if SEG.SEQ == RCV.NXT.
 		 * - If RST is in window, we send challenge ACK.
 		 *
-		 * Note: to take into account delayed ACKs, we should
-		 *   test against last_ack_sent instead of rcv_nxt.
+		 * Note 1: to take into account delayed ACKs, we should
+		 *   test against last_ack_sent in addition to rcv_nxt.
 		 * Note 2: we handle special case of closed window, not
 		 *   covered by the RFC.
+		 * Note 3 (XXXMT): check against rcv_adv instead of
+		 *   tp->rcv_nxt + tp->rcv_wnd.
 		 */
-		if ((SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
-		    SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) ||
-		    (tp->rcv_wnd == 0 && tp->last_ack_sent == th->th_seq)) {
+		if ((tp->rcv_wnd > 0 &&
+		     SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
+		     SEQ_LT(th->th_seq, tp->rcv_nxt + tp->rcv_wnd)) ||
+		    (tp->rcv_wnd == 0 &&
+		     (tp->last_ack_sent == th->th_seq ||
+		      tp->rcv_nxt == th->th_seq))) {
 			KASSERT(tp->t_state != TCPS_SYN_SENT,
 			    ("%s: TH_RST for TCPS_SYN_SENT th %p tp %p",
 			    __func__, th, tp));
 
 			if (V_tcp_insecure_rst ||
-			    tp->last_ack_sent == th->th_seq) {
+			    tp->last_ack_sent == th->th_seq ||
+			    tp->rcv_nxt == th->th_seq) {
 				TCPSTAT_INC(tcps_drops);
 				/* Drop the connection. */
 				switch (tp->t_state) {
diff --git a/sys/netinet/tcp_stacks/rack_bbr_common.c b/sys/netinet/tcp_stacks/rack_bbr_common.c
index 51120f96ebce..2022aeb7233e 100644
--- a/sys/netinet/tcp_stacks/rack_bbr_common.c
+++ b/sys/netinet/tcp_stacks/rack_bbr_common.c
@@ -697,18 +697,24 @@ ctf_process_rst(struct mbuf *m, struct tcphdr *th, struct socket *so,
 	/*
 	 * RFC5961 Section 3.2
 	 *
-	 * - RST drops connection only if SEG.SEQ == RCV.NXT. - If RST is in
-	 * window, we send challenge ACK.
+	 * - RST drops connection only if SEG.SEQ == RCV.NXT.
+	 * - If RST is in window, we send challenge ACK.
 	 *
-	 * Note: to take into account delayed ACKs, we should test against
-	 * last_ack_sent instead of rcv_nxt. Note 2: we handle special case
-	 * of closed window, not covered by the RFC.
+	 * Note 1: to take into account delayed ACKs, we should
+	 *   test against last_ack_sent in addition to rcv_nxt.
+	 * Note 2: we handle special case of closed window, not
+	 *   covered by the RFC.
+	 * Note 3 (XXXMT): check against rcv_adv instead of
+	 *   tp->rcv_nxt + tp->rcv_wnd.
 	 */
 	int dropped = 0;
 
-	if ((SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
-	    SEQ_LT(th->th_seq, tp->last_ack_sent + tp->rcv_wnd)) ||
-	    (tp->rcv_wnd == 0 && tp->last_ack_sent == th->th_seq)) {
+	if ((tp->rcv_wnd > 0 &&
+	     SEQ_GEQ(th->th_seq, tp->last_ack_sent) &&
+	     SEQ_LT(th->th_seq, tp->rcv_nxt + tp->rcv_wnd)) ||
+	    (tp->rcv_wnd == 0 &&
+	     (tp->last_ack_sent == th->th_seq ||
+	      tp->rcv_nxt == th->th_seq))) {
 		KASSERT(tp->t_state != TCPS_SYN_SENT,
 		    ("%s: TH_RST for TCPS_SYN_SENT th %p tp %p",
 		    __func__, th, tp));