Commit 1bc7c224 for openh264
commit 1bc7c2240d580ebd621a7575a6e68c4947691b7c
Author: BenzhengZhang <140143892+BenzhengZhang@users.noreply.github.com>
Date: Fri Aug 28 15:10:10 2026 +0800
decoder: harden bitstream tail reads without OOB (#3989)
Co-authored-by: benzzhan <benzzhan@cisco.com>
diff --git a/codec/decoder/core/inc/dec_golomb.h b/codec/decoder/core/inc/dec_golomb.h
index fef8df46..c26e5cb5 100644
--- a/codec/decoder/core/inc/dec_golomb.h
+++ b/codec/decoder/core/inc/dec_golomb.h
@@ -55,12 +55,19 @@ namespace WelsDec {
return uiRetTmp; \
}while(0)
#define GET_WORD(iCurBits, pBufPtr, iLeftBits, iAllowedBytes, iReadBytes) { \
- if (iReadBytes > iAllowedBytes+1) { \
+ if (iReadBytes > iAllowedBytes + 1) { \
return ERR_INFO_READ_OVERFLOW; \
} \
- iCurBits |= ((uint32_t)((pBufPtr[0] << 8) | pBufPtr[1])) << (iLeftBits); \
+ uint32_t uiWord = 0; \
+ if (iReadBytes < iAllowedBytes) { \
+ uiWord = ((uint32_t)pBufPtr[0]) << 8; \
+ } \
+ if ((iReadBytes + 1) < iAllowedBytes) { \
+ uiWord |= pBufPtr[1]; \
+ } \
+ iCurBits |= uiWord << (iLeftBits); \
iLeftBits -= 16; \
- pBufPtr +=2; \
+ pBufPtr += 2; \
}
#define NEED_BITS(iCurBits, pBufPtr, iLeftBits, iAllowedBytes, iReadBytes) { \
if (iLeftBits > 0) { \
diff --git a/codec/decoder/core/src/bit_stream.cpp b/codec/decoder/core/src/bit_stream.cpp
index c7526042..9dad4c25 100644
--- a/codec/decoder/core/src/bit_stream.cpp
+++ b/codec/decoder/core/src/bit_stream.cpp
@@ -48,11 +48,26 @@ inline uint32_t GetValue4Bytes (uint8_t* pDstNal) {
return uiValue;
}
+inline uint32_t GetValue4BytesSafe (uint8_t* pDstNal, intX_t iAvailableBytes) {
+ uint32_t uiValue = 0;
+ for (intX_t i = 0; i < 4; ++i) {
+ uiValue <<= 8;
+ if (i < iAvailableBytes) {
+ uiValue |= pDstNal[i];
+ }
+ }
+ return uiValue;
+}
+
int32_t InitReadBits (PBitStringAux pBitString, intX_t iEndOffset) {
- if (pBitString->pCurBuf >= (pBitString->pEndBuf - iEndOffset)) {
+ const intX_t kiAllowedBytes = (pBitString->pEndBuf - pBitString->pStartBuf) - iEndOffset;
+ const intX_t kiReadBytes = pBitString->pCurBuf - pBitString->pStartBuf;
+ const intX_t kiRemainBytes = kiAllowedBytes - kiReadBytes;
+ if (kiRemainBytes <= 0) {
return ERR_INFO_INVALID_ACCESS;
}
- pBitString->uiCurBits = GetValue4Bytes (pBitString->pCurBuf);
+ const intX_t kiSeedBytes = (kiRemainBytes < 4) ? kiRemainBytes : 4;
+ pBitString->uiCurBits = GetValue4BytesSafe (pBitString->pCurBuf, kiSeedBytes);
pBitString->pCurBuf += 4;
pBitString->iLeftBits = -16;
return ERR_NONE;
diff --git a/test/decoder/DecUT_ParseSyntax.cpp b/test/decoder/DecUT_ParseSyntax.cpp
index b19fee0f..694e1a45 100644
--- a/test/decoder/DecUT_ParseSyntax.cpp
+++ b/test/decoder/DecUT_ParseSyntax.cpp
@@ -651,4 +651,31 @@ TEST_F (DecoderParseSyntaxTest, ExpandBsBufferRetargetsQueuedNalUnitsOnly) {
Uninit();
}
+TEST (DecoderBitStreamBoundsTest, DecInitBitsHandlesShortSeedBytesSafely) {
+ uint8_t uiBuf[3] = {0xff, 0xff, 0xff};
+ SBitStringAux sBs;
+ memset (&sBs, 0, sizeof (sBs));
+
+ ASSERT_EQ (ERR_NONE, DecInitBits (&sBs, uiBuf, 24));
+
+ uint32_t uiCode = 0;
+ EXPECT_EQ (ERR_NONE, BsGetBits (&sBs, 16, &uiCode));
+ EXPECT_EQ (ERR_NONE, BsGetBits (&sBs, 16, &uiCode));
+ EXPECT_EQ (ERR_INFO_READ_OVERFLOW, BsGetBits (&sBs, 16, &uiCode));
+}
+
+TEST (DecoderBitStreamBoundsTest, BsGetBitsStopsOnTwoByteOverread) {
+ uint8_t uiBuf[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ SBitStringAux sBs;
+ memset (&sBs, 0, sizeof (sBs));
+
+ ASSERT_EQ (ERR_NONE, DecInitBits (&sBs, uiBuf, 48));
+
+ uint32_t uiCode = 0;
+ EXPECT_EQ (ERR_NONE, BsGetBits (&sBs, 16, &uiCode));
+ EXPECT_EQ (ERR_NONE, BsGetBits (&sBs, 16, &uiCode));
+ EXPECT_EQ (ERR_NONE, BsGetBits (&sBs, 16, &uiCode));
+ EXPECT_EQ (ERR_INFO_READ_OVERFLOW, BsGetBits (&sBs, 16, &uiCode));
+}
+