git: 9badd542e755 - releng/13.4 - sctp: improve input validation for data chunks

From: Michael Tuexen <tuexen_at_FreeBSD.org>
Date: Tue, 06 Aug 2024 22:38:08 UTC
The branch releng/13.4 has been updated by tuexen:

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

commit 9badd542e7552f9dfd4b868733a1e67c8f6df2df
Author:     Michael Tuexen <tuexen@FreeBSD.org>
AuthorDate: 2024-08-03 11:27:18 +0000
Commit:     Michael Tuexen <tuexen@FreeBSD.org>
CommitDate: 2024-08-06 22:36:18 +0000

    sctp: improve input validation for data chunks
    
    fsn_included should only be considered, if first_frag_seen is true.
    Also, fix the resetting of the control structure, if stream queues
    are flushed.
    This fixes a bug where a legitimate message sequence was incorrectly
    classified as illegitimate.
    Thanks to Victor Boivie for reporting the issue on the userland
    stack.
    
    (cherry picked from commit 101a0f09e8baf8293e1eeb591de18caf15e49e00)
    (cherry picked from commit 18f4b705734e1c76bac441ffe86cb8fbb131a153)
    
    Approved by:    re (cperciva)
---
 sys/netinet/sctp_indata.c | 35 +++++++++++++++++------------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/sys/netinet/sctp_indata.c b/sys/netinet/sctp_indata.c
index 4c40e0de4326..693de313b970 100644
--- a/sys/netinet/sctp_indata.c
+++ b/sys/netinet/sctp_indata.c
@@ -746,21 +746,6 @@ sctp_build_readq_entry_from_ctl(struct sctp_queued_to_read *nc, struct sctp_queu
 	nc->do_not_ref_stcb = control->do_not_ref_stcb;
 }
 
-static void
-sctp_reset_a_control(struct sctp_queued_to_read *control,
-    struct sctp_inpcb *inp, uint32_t tsn)
-{
-	control->fsn_included = tsn;
-	if (control->on_read_q) {
-		/*
-		 * We have to purge it from there, hopefully this will work
-		 * :-)
-		 */
-		TAILQ_REMOVE(&inp->read_queue, control, next);
-		control->on_read_q = 0;
-	}
-}
-
 static int
 sctp_handle_old_unordered_data(struct sctp_tcb *stcb,
     struct sctp_association *asoc,
@@ -1922,7 +1907,8 @@ sctp_process_a_data_chunk(struct sctp_tcb *stcb, struct sctp_association *asoc,
 				SCTP_SNPRINTF(msg, sizeof(msg), "Duplicate MID=%8.8x detected.", mid);
 				goto err_out;
 			} else {
-				if ((tsn == control->fsn_included + 1) &&
+				if ((control->first_frag_seen) &&
+				    (tsn == control->fsn_included + 1) &&
 				    (control->end_added == 0)) {
 					SCTP_SNPRINTF(msg, sizeof(msg),
 					    "Illegal message sequence, missing end for MID: %8.8x",
@@ -5430,12 +5416,25 @@ sctp_flush_reassm_for_str_seq(struct sctp_tcb *stcb,
 		sctp_free_a_chunk(stcb, chk, SCTP_SO_NOT_LOCKED);
 	}
 	if (!TAILQ_EMPTY(&control->reasm)) {
-		/* This has to be old data, unordered */
+		KASSERT(!asoc->idata_supported,
+		    ("Reassembly queue not empty for I-DATA"));
+		KASSERT(!ordered,
+		    ("Reassembly queue not empty for ordered data"));
 		if (control->data) {
 			sctp_m_freem(control->data);
 			control->data = NULL;
 		}
-		sctp_reset_a_control(control, stcb->sctp_ep, cumtsn);
+		control->fsn_included = 0xffffffff;
+		control->first_frag_seen = 0;
+		control->last_frag_seen = 0;
+		if (control->on_read_q) {
+			/*
+			 * We have to purge it from there, hopefully this
+			 * will work :-)
+			 */
+			TAILQ_REMOVE(&stcb->sctp_ep->read_queue, control, next);
+			control->on_read_q = 0;
+		}
 		chk = TAILQ_FIRST(&control->reasm);
 		if (chk->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG) {
 			TAILQ_REMOVE(&control->reasm, chk, sctp_next);