git: 429f2fb48b92 - main - sctp: hold tcp lock when calling sctp_calc_rwnd()

From: Michael Tuexen <tuexen_at_FreeBSD.org>
Date: Sun, 11 Oct 2026 20:05:21 UTC
The branch main has been updated by tuexen:

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

commit 429f2fb48b92011d07798e681d96aa628fd8e387
Author:     Lukas Book <lkbook@outlook.de>
AuthorDate: 2026-10-11 20:00:06 +0000
Commit:     Michael Tuexen <tuexen@FreeBSD.org>
CommitDate: 2026-10-11 20:00:06 +0000

    sctp: hold tcp lock when calling sctp_calc_rwnd()
    
    This was done in all code paths except one. Fix this to avoid
    race conditions.
    
    Reported by:    syzbot+2e2dc35c0e24a74663a7@syzkaller.appspotmail.com
    Reviewed by:    tuexen
    MFC after:      1 week
    MFC to:         stable/14
    MFC to:         stable/15
---
 sys/netinet/sctputil.c | 35 ++++++++++++++---------------------
 1 file changed, 14 insertions(+), 21 deletions(-)

diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index ddfa71d5c7ed..119fad13c3f0 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -5452,9 +5452,20 @@ sctp_user_rcvd(struct sctp_tcb *stcb, uint32_t *freed_so_far, int hold_rlock,
 		goto out;
 	}
 	atomic_add_int(&stcb->freed_by_sorcv_sincelast, *freed_so_far);
-	/* Have you have freed enough to look */
 	*freed_so_far = 0;
-	/* Yep, its worth a look and the lock overhead */
+	if (hold_rlock) {
+		SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
+		r_unlocked = 1;
+	}
+	if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
+		goto out;
+	}
+	SCTP_TCB_LOCK(stcb);
+	if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
+		/* No reports here */
+		SCTP_TCB_UNLOCK(stcb);
+		goto out;
+	}
 
 	/* Figure out what the rwnd would be */
 	rwnd = sctp_calc_rwnd(stcb, &stcb->asoc);
@@ -5464,24 +5475,6 @@ sctp_user_rcvd(struct sctp_tcb *stcb, uint32_t *freed_so_far, int hold_rlock,
 		dif = 0;
 	}
 	if (dif >= rwnd_req) {
-		if (hold_rlock) {
-			SCTP_INP_READ_UNLOCK(stcb->sctp_ep);
-			r_unlocked = 1;
-		}
-		if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
-			/*
-			 * One last check before we allow the guy possibly
-			 * to get in. There is a race, where the guy has not
-			 * reached the gate. In that case
-			 */
-			goto out;
-		}
-		SCTP_TCB_LOCK(stcb);
-		if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
-			/* No reports here */
-			SCTP_TCB_UNLOCK(stcb);
-			goto out;
-		}
 		SCTP_STAT_INCR(sctps_wu_sacks_sent);
 		NET_EPOCH_ENTER(et);
 		sctp_send_sack(stcb, SCTP_SO_LOCKED);
@@ -5492,11 +5485,11 @@ sctp_user_rcvd(struct sctp_tcb *stcb, uint32_t *freed_so_far, int hold_rlock,
 		NET_EPOCH_EXIT(et);
 		sctp_timer_stop(SCTP_TIMER_TYPE_RECV, stcb->sctp_ep, stcb, NULL,
 		    SCTP_FROM_SCTPUTIL + SCTP_LOC_6);
-		SCTP_TCB_UNLOCK(stcb);
 	} else {
 		/* Update how much we have pending */
 		stcb->freed_by_sorcv_sincelast = dif;
 	}
+	SCTP_TCB_UNLOCK(stcb);
 out:
 	if (so && r_unlocked && hold_rlock) {
 		SCTP_INP_READ_LOCK(stcb->sctp_ep);