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;