git: 9a406a9ec399 - main - tcp/rack: Fix a potential overflow in rack_update_pcm_ack()

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 14 Oct 2026 16:02:16 UTC
The branch main has been updated by markj:

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

commit 9a406a9ec399cced14ce5d204ba3fd383544b47f
Author:     Michael Tuexen <tuexen@FreeBSD.org>
AuthorDate: 2026-10-12 18:55:21 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-10-14 16:00:47 +0000

    tcp/rack: Fix a potential overflow in rack_update_pcm_ack()
    
    The RACK stack's path capacity measurement module maintains an array of
    samples, updated when the stack receives an ACK.  The array grows as
    needed, but code which handles the expansion is susceptible to remotely
    triggerable overflow.  This could lead to underallocation and a ~512KB
    out-of-bounds write.
    
    Fix this by bounding the sample array size.
    
    Approved by:    so
    Security:       FreeBSD-SA-26:70.tcp
    Security:       CVE-2026-101307
    Reviewed by:    glebius, markj
    Reported by:    Nozomi Networks Security Research
---
 sys/netinet/tcp_stacks/rack_pcm.c | 17 +++++++++++------
 sys/netinet/tcp_stacks/tcp_rack.h |  6 ++++--
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/sys/netinet/tcp_stacks/rack_pcm.c b/sys/netinet/tcp_stacks/rack_pcm.c
index 1a51097f627c..66297006000e 100644
--- a/sys/netinet/tcp_stacks/rack_pcm.c
+++ b/sys/netinet/tcp_stacks/rack_pcm.c
@@ -196,17 +196,22 @@ rack_update_pcm_ack(struct tcp_rack *rack, int was_cumack, uint32_t start, uint3
 		} else {
 			/* Need to expand the array */
 			struct rack_pcm_stats *n;
-			uint16_t new_cnt;
+			size_t new_cnt;
 
-			new_cnt = rack->r_ctl.pcm_i.cnt_alloc * 2;
-			sz = (sizeof(struct rack_pcm_stats) * new_cnt);
-			n = malloc(sz,M_TCPPCM, M_NOWAIT);
-			if (n == NULL) {
-				/* We are dead, no memory */
+			new_cnt = (size_t)rack->r_ctl.pcm_i.cnt_alloc * 2;
+			if (new_cnt > RACK_MAX_PCM_ARRAY) {
+				/* Too many entries required. */
+stop_measurement:
 				rack->pcm_in_progress = 0;
 				rack->r_ctl.pcm_i.cnt = 0;
 				return;
 			}
+			sz = (sizeof(struct rack_pcm_stats) * new_cnt);
+			n = malloc(sz, M_TCPPCM, M_NOWAIT);
+			if (n == NULL) {
+				/* We are dead, no memory. */
+				goto stop_measurement;
+			}
 			sz = (sizeof(struct rack_pcm_stats) * rack->r_ctl.pcm_i.cnt_alloc);
 			memcpy(n, rack->r_ctl.pcm_s, sz);
 			free(rack->r_ctl.pcm_s, M_TCPPCM);
diff --git a/sys/netinet/tcp_stacks/tcp_rack.h b/sys/netinet/tcp_stacks/tcp_rack.h
index 2e2ced089f67..f81d0a4b43fd 100644
--- a/sys/netinet/tcp_stacks/tcp_rack.h
+++ b/sys/netinet/tcp_stacks/tcp_rack.h
@@ -362,9 +362,11 @@ struct rack_pcm_info {
 	uint16_t cnt;
 	/* Maximum acks present */
 	uint16_t cnt_alloc;
+#define	RACK_DEFAULT_PCM_ARRAY	16
+#define	RACK_MAX_PCM_ARRAY	1024
 };
-
-#define RACK_DEFAULT_PCM_ARRAY 16
+_Static_assert(RACK_MAX_PCM_ARRAY <= UINT16_MAX,
+    "RACK_MAX_PCM_ARRAY must fit in 16 bits");
 
 struct rack_pcm_stats {
 	uint32_t sseq;