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;