Commit 536218a86b for asterisk.org

commit 536218a86b797996e8e8844deacc8207de373701
Author: George Joseph <gjoseph@sangoma.com>
Date:   Mon Apr 27 08:29:29 2026 -0600

    res_rtp_asterisk.c: Address 2 potential T.140 RED buffer overruns.

    * Add check to red_t140_to_red() to ensure that the new primary payload
    can't cause the rtp_red->len array items to wrap or cause an overrun of
    the rtp_red->t140red_data buffer.

    * Add check to rtp_red_buffer() to ensure that a T.140 frame to be sent
    can't cause rtp_red->len array items to wrap or cause an overrun of
    the rtp_red->buf_data buffer.

    Resolves: #GHSA-vfhr-r9x9-c687
    Resolves: #GHSA-j2mm-57pq-jh94

diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 38a494359a..f9d6983f89 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -5517,6 +5517,17 @@ static struct ast_frame *red_t140_to_red(struct rtp_red *red)
 	/* Store length of each generation and primary data length*/
 	for (i = 0; i < red->num_gen; i++)
 		red->len[i] = red->len[i+1];
+
+	/*
+	 * RED generation payload sizes are limited to 255 (UCHAR_MAX) bytes by virtue of
+	 * red->len being an array of usigned chars.  If the new primary payload exceeds that,
+	 * we're going to truncate it to 255.
+	 */
+	if (red->t140.datalen > UCHAR_MAX) {
+		ast_log(LOG_WARNING, "New T.140 frame of %d bytes exceeds max of %u. Discarding %d bytes.\n",
+			red->t140.datalen, UCHAR_MAX, red->t140.datalen - UCHAR_MAX);
+		red->t140.datalen = UCHAR_MAX;
+	}
 	red->len[i] = red->t140.datalen;

 	/* write each generation length in red header */
@@ -9301,7 +9312,13 @@ static int rtp_red_init(struct ast_rtp_instance *instance, int buffer_time, int
 	return 0;
 }

-/*! \pre instance is locked */
+/*! \pre instance is locked
+ *
+ * \warning This code was written many years ago and it's unclear why we actually
+ * buffer OUTGOING T.140 text frames until a command is encountered but we do.
+ *
+ * This may change in the future.
+ */
 static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *frame)
 {
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -9312,6 +9329,8 @@ static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *f
 	}

 	if (frame->datalen > 0) {
+		int space_available = 0;
+
 		if (red->t140.datalen > 0) {
 			const unsigned char *primary = red->buf_data;

@@ -9328,6 +9347,31 @@ static int rtp_red_buffer(struct ast_rtp_instance *instance, struct ast_frame *f
 			}
 		}

+		/*
+		 * RED generation payload sizes are limited to 255 (UCHAR_MAX) bytes by virtue of
+		 * red->len being an array of usigned chars.  If adding the current frame will
+		 * exceed that, we're going to flush the saved frame then try again. If the new
+		 * frame fits, great otherwise we're going to toss it.  Without understanding
+		 * the purpose of the buffering, that's all we can do now.
+		 */
+		space_available = UCHAR_MAX - red->t140.datalen;
+
+		if (frame->datalen > space_available) {
+			ast_rtp_write(instance, &rtp->red->t140);
+			/*
+			 * ast_rtp_write() calls red_t140_to_red() which resets red->t140.datalen
+			 * back to 0 so we now have UCHAR_MAX space available.
+			 */
+			space_available = UCHAR_MAX;
+		}
+
+		if (frame->datalen > space_available) {
+			ast_log(LOG_WARNING, "%s: T.140 frame of %d bytes exceeds max of %u. Discarding.\n",
+				ast_rtp_instance_get_channel_id(instance),
+				frame->datalen, UCHAR_MAX);
+			return -1;
+		}
+
 		memcpy(&red->buf_data[red->t140.datalen], frame->data.ptr, frame->datalen);
 		red->t140.datalen += frame->datalen;
 		red->t140.ts = frame->ts;