Commit 303b005f for openh264
commit 303b005fef767a121918ba41c14f791d23937734
Author: BenzhengZhang <140143892+BenzhengZhang@users.noreply.github.com>
Date: Mon Sep 14 16:07:27 2026 +0800
bound AVCC SPS/PPS walk against codec-specific data size (#3991)
* gmp: bound AVCC SPS/PPS walk against codec-specific data size
InitDecode() validated aCodecSpecificSize only once, as a one-time floor, then walked the AVCC blob's SPS/PPS counts and attacker-influenced 16-bit length fields via readU16BE()/copyWithStartCode() without ever re-checking that walk against the buffer end. Any codec-specific blob of exactly sizeof(GMPVideoCodecH264) bytes (the natural minimum) over-reads: with spsCount==1, readU16BE() reads 2 bytes past the end; with spsCount==0, the ppsCount read itself is 1 byte past the end. ASan-confirmed heap-buffer-overflow READ (CWE-125; root cause CWE-20).
Track a running end pointer (aCodecSpecific + aCodecSpecificSize) and bound every subsequent read against it: before consuming spsCount/ppsCount, before each readU16BE(), and before each copyWithStartCode(). On any violation, fail closed: log an error and skip SPS/PPS priming instead of continuing with a partially-parsed buffer. Also guard the existing &*annexb.begin() call so DecodeFrame2() is only invoked when annexb is non-empty.
Well-formed AVCC blobs are parsed identically to before (verified via the reporter's negative-control PoC mode). No public API/ABI change.
SPARK-843503
* Clean security comment wording
* Update gmp-openh264.cpp
---------
Co-authored-by: benzzhan <benzzhan@cisco.com>
diff --git a/module/gmp-openh264.cpp b/module/gmp-openh264.cpp
index 6354f315..940f3ab1 100644
--- a/module/gmp-openh264.cpp
+++ b/module/gmp-openh264.cpp
@@ -876,35 +876,62 @@ class OpenH264VideoDecoder : public GMPVideoDecoder, public RefCounted {
// Convert the AVCC data, starting at the byte containing
// numOfSequenceParameterSets, to Annex B format.
const uint8_t* avcc = aCodecSpecific + offsetof(GMPVideoCodecH264, mAVCC.mNumSPS);
+ // aCodecSpecificSize is only validated as a floor above.
+ // The AVCC SPS/PPS counts and 16-bit length fields must be validated,
+ // so bound every read against the end of the codec-specific buffer; the
+ // walking pointer must never read past the allocation.
+ const uint8_t* const avccEnd = aCodecSpecific + aCodecSpecificSize;
+ bool bAvccValid = true;
static const int kSPSMask = (1 << 5) - 1;
- uint8_t spsCount = *avcc++ & kSPSMask;
- for (int i = 0; i < spsCount; ++i) {
+ uint8_t spsCount = 0;
+ if (avcc < avccEnd) {
+ spsCount = *avcc++ & kSPSMask;
+ } else {
+ bAvccValid = false;
+ }
+ for (int i = 0; bAvccValid && i < spsCount; ++i) {
+ if (avccEnd - avcc < 2) { bAvccValid = false; break; }
size_t size = readU16BE(avcc);
avcc += 2;
+ if (size > static_cast<size_t> (avccEnd - avcc)) { bAvccValid = false; break; }
copyWithStartCode(annexb, avcc, size);
avcc += size;
}
- uint8_t ppsCount = *avcc++;
- for (int i = 0; i < ppsCount; ++i) {
+ uint8_t ppsCount = 0;
+ if (bAvccValid) {
+ if (avcc < avccEnd) {
+ ppsCount = *avcc++;
+ } else {
+ bAvccValid = false;
+ }
+ }
+ for (int i = 0; bAvccValid && i < ppsCount; ++i) {
+ if (avccEnd - avcc < 2) { bAvccValid = false; break; }
size_t size = readU16BE(avcc);
avcc += 2;
+ if (size > static_cast<size_t> (avccEnd - avcc)) { bAvccValid = false; break; }
copyWithStartCode(annexb, avcc, size);
avcc += size;
}
- SBufferInfo decoded;
- memset (&decoded, 0, sizeof (decoded));
- unsigned char* data[3] = {nullptr, nullptr, nullptr};
- DECODING_STATE dState = decoder_->DecodeFrame2 (&*annexb.begin(),
- annexb.size(),
- data,
- &decoded);
- if (dState) {
- GMPLOG (GL_ERROR, "Decoding error dState=" << dState);
+ if (!bAvccValid) {
+ GMPLOG (GL_ERROR, "InitDecode(): malformed AVCC extradata (size "
+ << aCodecSpecificSize << "); skipping SPS/PPS priming");
+ } else if (!annexb.empty()) {
+ SBufferInfo decoded;
+ memset (&decoded, 0, sizeof (decoded));
+ unsigned char* data[3] = {nullptr, nullptr, nullptr};
+ DECODING_STATE dState = decoder_->DecodeFrame2 (&*annexb.begin(),
+ annexb.size(),
+ data,
+ &decoded);
+ if (dState) {
+ GMPLOG (GL_ERROR, "Decoding error dState=" << dState);
+ }
+ GMPLOG (GL_ERROR, "InitDecode iBufferStatus=" << decoded.iBufferStatus);
}
- GMPLOG (GL_ERROR, "InitDecode iBufferStatus=" << decoded.iBufferStatus);
}
}