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));
+}
+