Commit a047b7a258 for freeswitch.com

commit a047b7a25818d952883d097ab8b8b0b099564ed7
Author: Dmitry Verenitsin <morbit85@gmail.com>
Date:   Sun Aug 9 00:55:35 2026 +0500

    [mod_rtmp] Harden H.264 video read path bounds and length parsing (#3113)

    `rtmp_rtmp2rtpH264` read the two leading bytes that select the parse
    branch (`data[0]` and `data[1]`) before any length check, so a video
    body shorter than 2 bytes read past the buffer. Reject `len < 2` at
    entry, before either classifier byte is dereferenced.

    In the NAL-unit branch, move the `pdata = data + 5` assignment below
    its `len < 5` check so the pointer is never formed past the end of a
    short buffer.

    Read the 2-byte big-endian SPS and PPS length prefixes byte-wise
    (`(pdata[0] << 8) | pdata[1]`) instead of `ntohs(*(uint16_t *)pdata)`.
    `pdata` walks a byte buffer at wire-controlled offsets, so the cast was
    an unaligned 16-bit load and a strict-aliasing violation; the byte-wise
    read is alignment- and endianness-independent and matches the idiom
    already used in the NAL-unit branch.

    In `rtmp_read_video_frame`, a buffered record is `len + 6` bytes (2-byte
    length prefix, 4-byte timestamp, `len`-byte body), but the guard only
    required `inuse >= len` before consuming the whole record. Require
    `inuse >= len + 6` so the invariant holds locally instead of relying on
    the writer emitting each record atomically.

diff --git a/src/mod/endpoints/mod_rtmp/rtmp_video.c b/src/mod/endpoints/mod_rtmp/rtmp_video.c
index 5365fe830c..547c99ec7e 100644
--- a/src/mod/endpoints/mod_rtmp/rtmp_video.c
+++ b/src/mod/endpoints/mod_rtmp/rtmp_video.c
@@ -126,6 +126,13 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 	switch_status_t status = SWITCH_STATUS_SUCCESS;
 	uint8_t *end = data + len;

+	/* both classifier bytes must be present before dereferencing them */
+	if (len < 2) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
+
+		return SWITCH_STATUS_FALSE;
+	}
+
 	if (data[0] == 0x17 && data[1] == 0) {
 		switch_byte_t *pdata = data + 2;
 		int cfgVer;
@@ -154,7 +161,7 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 					return SWITCH_STATUS_FALSE;
 				}

-				lenSPS = ntohs(*(uint16_t *)pdata);
+				lenSPS = (pdata[0] << 8) | pdata[1];
 				pdata += 2;

 				if (lenSPS > end - pdata) {
@@ -183,7 +190,7 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 					return SWITCH_STATUS_FALSE;
 				}

-				lenPPS = ntohs(*(uint16_t *)pdata);
+				lenPPS = (pdata[0] << 8) | pdata[1];
 				pdata += 2;
 				if (lenPPS > end - pdata) {
 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "corrupted data\n");
@@ -220,7 +227,7 @@ 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;
+			switch_byte_t *pdata;
 			uint32_t  pdata_len;
 			uint32_t  lenSize = read_helper->lenSize;
 			switch_byte_t  *nal_buf = NULL;
@@ -232,6 +239,7 @@ switch_status_t rtmp_rtmp2rtpH264(rtmp2rtp_helper_t  *read_helper, uint8_t* data
 				return SWITCH_STATUS_FALSE;
 			}

+			pdata = data + 5;
 			pdata_len = len - 5;

 			while (pdata_len > lenSize) {
@@ -769,7 +777,7 @@ switch_status_t rtmp_read_video_frame(switch_core_session_t *session, switch_fra
 	} else {
 		switch_mutex_lock(tech_pvt->video_readbuf_mutex);
 		switch_buffer_peek(tech_pvt->video_readbuf, &len, 2);
-		if (switch_buffer_inuse(tech_pvt->video_readbuf) >= len) {
+		if (switch_buffer_inuse(tech_pvt->video_readbuf) >= (switch_size_t)len + 6) {
 			if (len == 0) {
 				switch_mutex_unlock(tech_pvt->video_readbuf_mutex);
 				switch_yield(20000);