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.
This commit is contained in:
Dmitry Verenitsin
2026-08-08 20:01:20 +03:00
committed by GitHub
parent fc0db829b8
commit 72cda5bfad
+44 -3
View File
@@ -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;