Commit 72cda5bfad for freeswitch.com

commit 72cda5bfad67d14d8dbe76b8f8bdd38c4fa5c932
Author: Dmitry Verenitsin <morbit85@gmail.com>
Date:   Sat Aug 8 22:01:20 2026 +0500

    Merge commit from fork

    `rtmp_rtmp2rtpH264()` parses two kinds of inbound H.264 RTMP video messages: an
    AVC configuration record (`0x17/0x00`) that captures SPS/PPS, and NAL-unit
    messages (`0x17`/`0x27` with `0x01`). Both paths trusted wire-supplied sizes and
    counts without checking them against the bytes actually present, leading to
    out-of-bounds reads.

    NAL-unit walk:

    - The length-prefix walk advanced the cursor and decremented the unsigned
      remaining-byte counter by the wire NAL size with no check that the size fit.
      A NAL size larger than the remaining payload underflowed the counter to near
      `UINT32_MAX`, kept the loop running, and read the next size prefix from a
      cursor already past the end of the buffer. The initializer
      `pdata_len = len - 5` underflowed the same way for a message shorter than the
      5-byte AVC header.
    - Reject `len < 5`, change the loop guard to `pdata_len > lenSize` so each
      size-prefix read stays in bounds, and reject any NAL whose declared size
      exceeds the bytes remaining after its prefix.

    AVC configuration record:

    - The fixed header fields (`configurationVersion`, `lengthSizeMinusOne`,
      `numOfSequenceParameterSets`) plus each 2-byte SPS/PPS length prefix and the
      PPS count byte were read with no minimum-length check. The existing per-entry
      checks bounded only the SPS/PPS body copies and ran after the length reads.
    - Reject `len < 11` before the fixed header, and add a remaining-bytes check
      before each `ntohs` length read and before the PPS count byte.

    Both changes are correctness-only: well-formed records and NAL streams hit none
    of the new guards. Malformed or truncated input is rejected with the existing
    "corrupted data" diagnostic.

diff --git a/src/mod/endpoints/mod_rtmp/rtmp_video.c b/src/mod/endpoints/mod_rtmp/rtmp_video.c
index 6b9ca84b96..002d4e890b 100644
--- a/src/mod/endpoints/mod_rtmp/rtmp_video.c
+++ b/src/mod/endpoints/mod_rtmp/rtmp_video.c
@@ -128,7 +128,15 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data

 	if (data[0] == 0x17 && data[1] == 0) {
 		switch_byte_t *pdata = data + 2;
-		int cfgVer = pdata[3];
+		int cfgVer;
+
+		if (len < 11) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
+
+			return SWITCH_STATUS_FALSE;
+		}
+
+		cfgVer = pdata[3];
 		if (cfgVer == 1) {
 			int i = 0;
 			int numSPS = 0;
@@ -140,6 +148,12 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 			numSPS = pdata[8] & 0x1f;
 			pdata += 9;
 			for (i = 0; i < numSPS; i++) {
+				if (end - pdata < 2) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
+
+					return SWITCH_STATUS_FALSE;
+				}
+
 				lenSPS = ntohs(*(uint16_t *)pdata);
 				pdata += 2;

@@ -154,9 +168,21 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 				pdata += lenSPS;
 			}
 			//pps
+			if (end - pdata < 1) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
+
+				return SWITCH_STATUS_FALSE;
+			}
+
 			numPPS = pdata[0];
 			pdata += 1;
 			for (i = 0; i < numPPS; i++) {
+				if (end - pdata < 2) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
+
+					return SWITCH_STATUS_FALSE;
+				}
+
 				lenPPS = ntohs(*(uint16_t *)pdata);
 				pdata += 2;
 				if (lenPPS > end - pdata) {
@@ -195,12 +221,20 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 	} else if ((data[0] == 0x17 || data[0] == 0x27) && data[1] == 1) {
 		if (read_helper->sps && read_helper->pps) {
 			switch_byte_t * pdata = data + 5;
-			uint32_t  pdata_len = len - 5;
+			uint32_t  pdata_len;
 			uint32_t  lenSize = read_helper->lenSize;
 			switch_byte_t  *nal_buf = NULL;
 			uint32_t        nal_len = 0;

-			while (pdata_len > 0) {
+			if (len < 5) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
+
+				return SWITCH_STATUS_FALSE;
+			}
+
+			pdata_len = len - 5;
+
+			while (pdata_len > lenSize) {
 				uint32_t nalSize = 0;
 				switch (lenSize) {
 				case 1:
@@ -220,6 +254,13 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 					return SWITCH_STATUS_FALSE;
 				}

+				/* reject a NAL that claims more bytes than remain after its length prefix */
+				if (nalSize > pdata_len - lenSize) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
+
+					return SWITCH_STATUS_FALSE;
+				}
+
 				nal_buf = pdata + lenSize;
 				nal_len = nalSize;