Commit 772e63d3 for openh264

commit 772e63d37344dc9159aec9ce1b97708c48b154dd
Author: BenzhengZhang <140143892+BenzhengZhang@users.noreply.github.com>
Date:   Tue Aug 25 16:42:10 2026 +0800

    security: fix ExpandBsBuffer queued retarget UAF (SPARK-838034) (#3985)

    Co-authored-by: benzzhan <benzzhan@cisco.com>

diff --git a/codec/decoder/core/src/decoder_core.cpp b/codec/decoder/core/src/decoder_core.cpp
index 65df2067..3bde051c 100644
--- a/codec/decoder/core/src/decoder_core.cpp
+++ b/codec/decoder/core/src/decoder_core.cpp
@@ -661,9 +661,16 @@ int32_t ExpandBsBuffer (PWelsDecoderContext pCtx, const int kiSrcLen) {
     return ERR_INFO_OUT_OF_MEMORY;
   }

-  //Calculate and set the bs start and end position
-  for (uint32_t i = 0; i <= pCtx->pAccessUnitList->uiActualUnitsNum; i++) {
-    PBitStringAux pSliceBitsRead = &pCtx->pAccessUnitList->pNalUnitsList[i]->sNalData.sVclNal.sSliceBitsRead;
+  // Retarget all queued NAL units in current AU list. uiAvailUnitsNum is the
+  // queued-NAL count that can be consumed later; using uiActualUnitsNum here
+  // can leave queued entries pointing to freed old buffer.
+  const uint32_t kuiRetargetNum = pCtx->pAccessUnitList->uiAvailUnitsNum;
+  for (uint32_t i = 0; i < kuiRetargetNum; ++i) {
+    PNalUnit pNal = pCtx->pAccessUnitList->pNalUnitsList[i];
+    if (pNal == NULL) {
+      continue;
+    }
+    PBitStringAux pSliceBitsRead = &pNal->sNalData.sVclNal.sSliceBitsRead;
     pSliceBitsRead->pStartBuf = pSliceBitsRead->pStartBuf - pCtx->sRawData.pHead + pNewBsBuff;
     pSliceBitsRead->pEndBuf   = pSliceBitsRead->pEndBuf   - pCtx->sRawData.pHead + pNewBsBuff;
     pSliceBitsRead->pCurBuf   = pSliceBitsRead->pCurBuf   - pCtx->sRawData.pHead + pNewBsBuff;
diff --git a/test/decoder/DecUT_ParseSyntax.cpp b/test/decoder/DecUT_ParseSyntax.cpp
index ea2f076a..b19fee0f 100644
--- a/test/decoder/DecUT_ParseSyntax.cpp
+++ b/test/decoder/DecUT_ParseSyntax.cpp
@@ -601,4 +601,54 @@ TEST (DecoderReorderingBufferTest, FullResetInitializesPicBuffIdx) {
   EXPECT_EQ (0, sStatus.iLargestBufferedPicIndex);
 }

+TEST_F (DecoderParseSyntaxTest, ExpandBsBufferRetargetsQueuedNalUnitsOnly) {
+  ASSERT_EQ (ERR_NONE, Init());
+  ASSERT_TRUE (m_pCtx != NULL);
+  ASSERT_TRUE (m_pCtx->pAccessUnitList != NULL);
+
+  PAccessUnit pAu = m_pCtx->pAccessUnitList;
+  ASSERT_TRUE (pAu->uiCountUnitsNum >= 3);
+  ASSERT_TRUE (m_pCtx->sRawData.pHead != NULL);
+
+  uint8_t* pOldHead = m_pCtx->sRawData.pHead;
+  PBitStringAux pActual = &pAu->pNalUnitsList[0]->sNalData.sVclNal.sSliceBitsRead;
+  pActual->pStartBuf = pOldHead + 8;
+  pActual->pCurBuf = pOldHead + 12;
+  pActual->pEndBuf = pOldHead + 16;
+
+  PBitStringAux pQueued = &pAu->pNalUnitsList[1]->sNalData.sVclNal.sSliceBitsRead;
+  pQueued->pStartBuf = pOldHead + 24;
+  pQueued->pCurBuf = pOldHead + 28;
+  pQueued->pEndBuf = pOldHead + 32;
+
+  PBitStringAux pOnePastAvail = &pAu->pNalUnitsList[2]->sNalData.sVclNal.sSliceBitsRead;
+  pOnePastAvail->pStartBuf = pOldHead + 40;
+  pOnePastAvail->pCurBuf = pOldHead + 44;
+  pOnePastAvail->pEndBuf = pOldHead + 48;
+  uint8_t* pOnePastStartBefore = pOnePastAvail->pStartBuf;
+  uint8_t* pOnePastCurBefore = pOnePastAvail->pCurBuf;
+  uint8_t* pOnePastEndBefore = pOnePastAvail->pEndBuf;
+
+  pAu->uiAvailUnitsNum = 2;
+  pAu->uiActualUnitsNum = 1; // queued count is still 2, so index 1 must be retargeted
+
+  ASSERT_EQ (ERR_NONE, ExpandBsBuffer (m_pCtx, m_pCtx->iMaxBsBufferSizeInByte));
+  ASSERT_TRUE (m_pCtx->sRawData.pHead != pOldHead);
+
+  EXPECT_EQ (m_pCtx->sRawData.pHead + 8, pActual->pStartBuf);
+  EXPECT_EQ (m_pCtx->sRawData.pHead + 12, pActual->pCurBuf);
+  EXPECT_EQ (m_pCtx->sRawData.pHead + 16, pActual->pEndBuf);
+
+  EXPECT_EQ (m_pCtx->sRawData.pHead + 24, pQueued->pStartBuf);
+  EXPECT_EQ (m_pCtx->sRawData.pHead + 28, pQueued->pCurBuf);
+  EXPECT_EQ (m_pCtx->sRawData.pHead + 32, pQueued->pEndBuf);
+
+  // Slot at index uiAvailUnitsNum (one-past queued range) must not be touched.
+  EXPECT_EQ (pOnePastStartBefore, pOnePastAvail->pStartBuf);
+  EXPECT_EQ (pOnePastCurBefore, pOnePastAvail->pCurBuf);
+  EXPECT_EQ (pOnePastEndBefore, pOnePastAvail->pEndBuf);
+
+  Uninit();
+}
+