git: 66efc521e0ff - releng/14.4 - libalias: Fix buffer overflow in RTSP aliasing

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Tue, 30 Jun 2026 17:20:57 UTC
The branch releng/14.4 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=66efc521e0ffbed1fd6d8481d7a7a66be3e56571

commit 66efc521e0ffbed1fd6d8481d7a7a66be3e56571
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2026-06-28 22:10:40 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-06-29 19:17:30 +0000

    libalias: Fix buffer overflow in RTSP aliasing
    
    In alias_rtsp_out(), we construct a new packet in a fixed-length buffer
    before copying it back into the original buffer.  We never checked if
    the data we were writing to this temporary buffer fit in it, nor if the
    result fit in the original buffer.
    
    * Use a dynamically sized allocated buffer instead of a fixed-length
      stack buffer.
    
    * Check for overflow before appending to our temporary buffer.
    
    * Check for overflow before copying the data back to the packet buffer.
    
    * While here, use size_t for sizes and bool for booleans.
    
    Approved by:    so
    Security:       FreeBSD-SA-26:41.libalias
    Security:       CVE-2026-49420
    MFC after:      1 week
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D57753
---
 sys/netinet/libalias/alias_smedia.c | 156 ++++++++++++++++++++----------------
 1 file changed, 89 insertions(+), 67 deletions(-)

diff --git a/sys/netinet/libalias/alias_smedia.c b/sys/netinet/libalias/alias_smedia.c
index 6c67e0d8f006..af1f6ed4aca8 100644
--- a/sys/netinet/libalias/alias_smedia.c
+++ b/sys/netinet/libalias/alias_smedia.c
@@ -104,8 +104,9 @@
 #include <sys/kernel.h>
 #include <sys/module.h>
 #else
-#include <errno.h>
 #include <sys/types.h>
+#include <errno.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
 #endif
@@ -128,9 +129,9 @@
 #define RTSP_CONTROL_PORT_NUMBER_2 7070
 #define TFTP_PORT_NUMBER 69
 
-static void
-AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *,
-    int maxpacketsize);
+static void AliasHandleRtspOut(struct libalias *, struct ip *,
+    struct alias_link *, size_t);
+
 static int
 fingerprint(struct libalias *la, struct alias_data *ah)
 {
@@ -154,7 +155,8 @@ protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
 	if (ntohs(*ah->dport) == TFTP_PORT_NUMBER)
 		FindRtspOut(la, pip->ip_src, pip->ip_dst,
 		    *ah->sport, *ah->aport, IPPROTO_UDP);
-	else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize);
+	else
+		AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize);
 	return (0);
 }
 
@@ -208,13 +210,15 @@ MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1);
 
 #define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
 
-static int
-search_string(char *data, int dlen, const char *search_str)
+static ssize_t
+search_string(char *data, size_t dlen, const char *search_str)
 {
-	int i, j, k;
-	int search_str_len;
+	size_t i, j, k;
+	size_t search_str_len;
 
 	search_str_len = strlen(search_str);
+	if (search_str_len > dlen)
+		return (-1);
 	for (i = 0; i < dlen - search_str_len; i++) {
 		for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
 			if (data[j] != search_str[k] &&
@@ -230,18 +234,21 @@ search_string(char *data, int dlen, const char *search_str)
 static int
 alias_rtsp_out(struct libalias *la, struct ip *pip,
     struct alias_link *lnk,
-    char *data,
+    char *data, size_t maxlen,
     const char *port_str)
 {
-	int hlen, tlen, dlen;
+	size_t hlen, tlen, dlen;
+	size_t i, j;
 	struct tcphdr *tc;
-	int i, j, pos, state, port_dlen, new_dlen, delta;
+	int delta, state;
+	ssize_t pos, slen;
+	size_t new_dlen, port_dlen, port_slen;
 	u_short p[2], new_len;
 	u_short sport, eport, base_port;
 	u_short salias = 0, ealias = 0, base_alias = 0;
 	const char *transport_str = "transport:";
-	char newdata[2048], *port_data, *port_newdata, stemp[80];
-	int links_created = 0, pkt_updated = 0;
+	char *newdata, *port_data;
+	bool links_created = false, pkt_updated = false;
 	struct alias_link *rtsp_lnk = NULL;
 	struct in_addr null_addr;
 
@@ -250,6 +257,9 @@ alias_rtsp_out(struct libalias *la, struct ip *pip,
 	hlen = (pip->ip_hl + tc->th_off) << 2;
 	tlen = ntohs(pip->ip_len);
 	dlen = tlen - hlen;
+	if (hlen > tlen || tlen > maxlen)
+		return (-1);
+	port_slen = strlen(port_str);
 
 	/* Find keyword, "Transport: " */
 	pos = search_string(data, dlen, transport_str);
@@ -259,17 +269,21 @@ alias_rtsp_out(struct libalias *la, struct ip *pip,
 	port_data = data + pos;
 	port_dlen = dlen - pos;
 
+	/* Allocate temporary buffer */
+	maxlen -= hlen;
+	if ((newdata = malloc(maxlen)) == NULL)
+		return (-1);
 	memcpy(newdata, data, pos);
-	port_newdata = newdata + pos;
+	new_dlen = pos;
 
-	while (port_dlen > (int)strlen(port_str)) {
+	while (port_dlen > port_slen) {
 		/* Find keyword, appropriate port string */
 		pos = search_string(port_data, port_dlen, port_str);
-		if (pos < 0)
+		if (pos < 0 || (size_t)pos + 1 > maxlen - new_dlen)
 			break;
 
-		memcpy(port_newdata, port_data, pos + 1);
-		port_newdata += (pos + 1);
+		memcpy(newdata + new_dlen, port_data, pos + 1);
+		new_dlen += pos + 1;
 
 		p[0] = p[1] = 0;
 		sport = eport = 0;
@@ -300,7 +314,7 @@ alias_rtsp_out(struct libalias *la, struct ip *pip,
 				eport = htons(p[1]);
 
 				if (!links_created) {
-					links_created = 1;
+					links_created = true;
 					/*
 					 * Find an even numbered port
 					 * number base that satisfies the
@@ -349,24 +363,30 @@ alias_rtsp_out(struct libalias *la, struct ip *pip,
 					ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
 				}
 				if (salias && rtsp_lnk) {
-					pkt_updated = 1;
+					pkt_updated = true;
 
 					/* Copy into IP packet */
-					sprintf(stemp, "%d", ntohs(salias));
-					memcpy(port_newdata, stemp, strlen(stemp));
-					port_newdata += strlen(stemp);
+					slen = snprintf(newdata + new_dlen,
+					    maxlen - new_dlen, "%d", ntohs(salias));
+					if (slen < 0 || slen >= maxlen - new_dlen)
+						goto fail;
+					new_dlen += slen;
 
 					if (eport != 0) {
-						*port_newdata = '-';
-						port_newdata++;
+						if (new_dlen == maxlen)
+							goto fail;
+						newdata[new_dlen++] = '-';
 
 						/* Copy into IP packet */
-						sprintf(stemp, "%d", ntohs(ealias));
-						memcpy(port_newdata, stemp, strlen(stemp));
-						port_newdata += strlen(stemp);
+						slen = snprintf(newdata + new_dlen,
+						    maxlen - new_dlen, "%d", ntohs(ealias));
+						if (slen < 0 || slen >= maxlen - new_dlen)
+							goto fail;
+						new_dlen += slen;
 					}
-					*port_newdata = ';';
-					port_newdata++;
+					if (new_dlen == maxlen)
+						goto fail;
+					newdata[new_dlen++] = ';';
 				}
 				state++;
 				break;
@@ -380,20 +400,20 @@ alias_rtsp_out(struct libalias *la, struct ip *pip,
 	}
 
 	if (!pkt_updated)
-		return (-1);
-
-	memcpy(port_newdata, port_data, port_dlen);
-	port_newdata += port_dlen;
-	*port_newdata = '\0';
+		goto fail;
 
 	/* Create new packet */
-	new_dlen = port_newdata - newdata;
+	if (new_dlen + port_dlen > maxlen)
+		goto fail;
+	memmove(data + new_dlen, port_data, port_dlen);
 	memcpy(data, newdata, new_dlen);
+	new_dlen += port_dlen;
+	free(newdata);
 
 	SetAckModified(lnk);
 	tc = (struct tcphdr *)ip_next(pip);
 	delta = GetDeltaSeqOut(tc->th_seq, lnk);
-	AddSeq(lnk, delta + new_dlen - dlen, pip->ip_hl, pip->ip_len,
+	AddSeq(lnk, delta + (int)(new_dlen - dlen), pip->ip_hl, pip->ip_len,
 	    tc->th_seq, tc->th_off);
 
 	new_len = htons(hlen + new_dlen);
@@ -407,6 +427,9 @@ alias_rtsp_out(struct libalias *la, struct ip *pip,
 	tc->th_sum = TcpChecksum(pip);
 #endif
 	return (0);
+fail:
+	free(newdata);
+	return (-1);
 }
 
 /* Support the protocol used by early versions of RealPlayer */
@@ -415,13 +438,13 @@ static int
 alias_pna_out(struct libalias *la, struct ip *pip,
     struct alias_link *lnk,
     char *data,
-    int dlen)
+    size_t dlen)
 {
 	struct alias_link *pna_links;
-	u_short msg_id, msg_len;
 	char *work;
-	u_short alias_port, port;
 	struct tcphdr *tc;
+	u_short msg_id, msg_len;
+	u_short alias_port, port;
 
 	work = data;
 	work += 5;
@@ -433,7 +456,7 @@ alias_pna_out(struct libalias *la, struct ip *pip,
 		if (ntohs(msg_id) == 0) /* end of options */
 			return (0);
 
-		if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
+		if (ntohs(msg_id) == 1 || ntohs(msg_id) == 7) {
 			memcpy(&port, work, 2);
 			pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
 			    port, 0, IPPROTO_UDP, 1);
@@ -462,22 +485,23 @@ alias_pna_out(struct libalias *la, struct ip *pip,
 }
 
 static void
-AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
+AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk,
+    size_t maxlen)
 {
-	int hlen, tlen, dlen;
 	struct tcphdr *tc;
 	char *data;
 	const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
 	const char *okstr = "OK", *client_port_str = "client_port";
 	const char *server_port_str = "server_port";
-	int i, parseOk;
-
-	(void)maxpacketsize;
+	size_t hlen, tlen, dlen;
+	size_t i;
 
 	tc = (struct tcphdr *)ip_next(pip);
 	hlen = (pip->ip_hl + tc->th_off) << 2;
 	tlen = ntohs(pip->ip_len);
 	dlen = tlen - hlen;
+	if (hlen > tlen || tlen > maxlen)
+		return;
 
 	data = (char *)pip;
 	data += hlen;
@@ -485,13 +509,14 @@ AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk,
 	/* When aliasing a client, check for the SETUP request */
 	if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
 	    (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
-		if (dlen >= (int)strlen(setup) &&
+		if (dlen >= strlen(setup) &&
 		    memcmp(data, setup, strlen(setup)) == 0) {
-			alias_rtsp_out(la, pip, lnk, data, client_port_str);
+			alias_rtsp_out(la, pip, lnk, data, maxlen,
+			    client_port_str);
 			return;
 		}
 
-		if (dlen >= (int)strlen(pna) &&
+		if (dlen >= strlen(pna) &&
 		    memcmp(data, pna, strlen(pna)) == 0)
 			alias_pna_out(la, pip, lnk, data, dlen);
 	} else {
@@ -499,24 +524,21 @@ AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk,
 		 * When aliasing a server, check for the 200 reply
 		 * Accommodate varying number of blanks between 200 & OK
 		 */
-
-		if (dlen >= (int)strlen(str200)) {
-			for (parseOk = 0, i = 0;
-			    i <= dlen - (int)strlen(str200);
-			    i++)
-				if (memcmp(&data[i], str200, strlen(str200)) == 0) {
-					parseOk = 1;
-					break;
-				}
-
-			if (parseOk) {
-				i += strlen(str200);	/* skip string found */
-				while (data[i] == ' ')	/* skip blank(s) */
+		if (dlen < strlen(str200) + 1 + strlen(okstr))
+			return;
+		for (i = 0; i <= dlen - strlen(str200) - 1; i++) {
+			if (memcmp(&data[i], str200, strlen(str200)) == 0 &&
+			    data[i + strlen(str200)] == ' ') {
+				i += strlen(str200);	/* skip 200 */
+				while (i < dlen && data[i] == ' ')	/* skip blank(s) */
 					i++;
-
-				if ((dlen - i) >= (int)strlen(okstr))
-					if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
-						alias_rtsp_out(la, pip, lnk, data, server_port_str);
+				if (dlen - i >= strlen(okstr)) {
+					if (memcmp(&data[i], okstr, strlen(okstr)) == 0) {
+						alias_rtsp_out(la, pip, lnk, data,
+						    maxlen, server_port_str);
+						break;
+					}
+				}
 			}
 		}
 	}