Commit 3137f7f1 for openh264

commit 3137f7f18fd3a166ed7f282cb917cd9a83ecc6ee
Author: BenzhengZhang <140143892+BenzhengZhang@users.noreply.github.com>
Date:   Fri Aug 28 16:09:30 2026 +0800

    decoder: validate EC copy source/destination compatibility (#3990)

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

diff --git a/codec/decoder/core/src/error_concealment.cpp b/codec/decoder/core/src/error_concealment.cpp
index 606c49ad..768f45b5 100644
--- a/codec/decoder/core/src/error_concealment.cpp
+++ b/codec/decoder/core/src/error_concealment.cpp
@@ -39,6 +39,40 @@
 #include "cpu_core.h"

 namespace WelsDec {
+
+static inline int32_t GetEcPicWidthInPixel (const PWelsDecoderContext pCtx, const PPicture pPic) {
+  if (pPic != NULL && pPic->iWidthInPixel > 0) {
+    return pPic->iWidthInPixel;
+  }
+  return pCtx->pSps->iMbWidth << 4;
+}
+
+static inline int32_t GetEcPicHeightInPixel (const PWelsDecoderContext pCtx, const PPicture pPic) {
+  if (pPic != NULL && pPic->iHeightInPixel > 0) {
+    return pPic->iHeightInPixel;
+  }
+  return pCtx->pSps->iMbHeight << 4;
+}
+
+static inline bool IsEcRefPicCompatible (const PWelsDecoderContext pCtx, const PPicture pDstPic, const PPicture pSrcPic) {
+  if (pDstPic == NULL || pSrcPic == NULL) {
+    return false;
+  }
+
+  const int32_t iDstWidthInPixel = GetEcPicWidthInPixel (pCtx, pDstPic);
+  const int32_t iDstHeightInPixel = GetEcPicHeightInPixel (pCtx, pDstPic);
+  const int32_t iSrcWidthInPixel = GetEcPicWidthInPixel (pCtx, pSrcPic);
+  const int32_t iSrcHeightInPixel = GetEcPicHeightInPixel (pCtx, pSrcPic);
+
+  if (iDstWidthInPixel != iSrcWidthInPixel || iDstHeightInPixel != iSrcHeightInPixel) {
+    return false;
+  }
+
+  return pSrcPic->iLinesize[0] >= pDstPic->iLinesize[0]
+         && pSrcPic->iLinesize[1] >= pDstPic->iLinesize[1]
+         && pSrcPic->iLinesize[2] >= pDstPic->iLinesize[2];
+}
+
 //Init
 void InitErrorCon (PWelsDecoderContext pCtx) {
   if ((pCtx->pParam->eEcActiveIdc == ERROR_CON_SLICE_COPY)
@@ -91,22 +125,25 @@ void InitErrorCon (PWelsDecoderContext pCtx) {
 void DoErrorConFrameCopy (PWelsDecoderContext pCtx) {
   PPicture pDstPic = pCtx->pDec;
   PPicture pSrcPic = pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb;
-  uint32_t uiHeightInPixelY = (pCtx->pSps->iMbHeight) << 4;
+  int32_t iHeightInPixelY = GetEcPicHeightInPixel (pCtx, pDstPic);
   int32_t iStrideY = pDstPic->iLinesize[0];
   int32_t iStrideUV = pDstPic->iLinesize[1];
   pCtx->pDec->iMbEcedNum = pCtx->pSps->iMbWidth * pCtx->pSps->iMbHeight;
   if ((pCtx->pParam->eEcActiveIdc == ERROR_CON_FRAME_COPY) && (pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag))
     pSrcPic = NULL; //no cross IDR method, should fill in data instead of copy
+  if (!IsEcRefPicCompatible (pCtx, pDstPic, pSrcPic)) {
+    pSrcPic = NULL;
+  }
   if (pSrcPic == NULL) { //no ref pic, assign specific data to picture
-    memset (pDstPic->pData[0], 128, uiHeightInPixelY * iStrideY);
-    memset (pDstPic->pData[1], 128, (uiHeightInPixelY >> 1) * iStrideUV);
-    memset (pDstPic->pData[2], 128, (uiHeightInPixelY >> 1) * iStrideUV);
+    memset (pDstPic->pData[0], 128, iHeightInPixelY * iStrideY);
+    memset (pDstPic->pData[1], 128, (iHeightInPixelY >> 1) * iStrideUV);
+    memset (pDstPic->pData[2], 128, (iHeightInPixelY >> 1) * iStrideUV);
   } else if (pSrcPic == pDstPic) {
     WelsLog (& (pCtx->sLogCtx), WELS_LOG_WARNING, "DoErrorConFrameCopy()::EC memcpy overlap.");
   } else { //has ref pic here
-    memcpy (pDstPic->pData[0], pSrcPic->pData[0], uiHeightInPixelY * iStrideY);
-    memcpy (pDstPic->pData[1], pSrcPic->pData[1], (uiHeightInPixelY >> 1) * iStrideUV);
-    memcpy (pDstPic->pData[2], pSrcPic->pData[2], (uiHeightInPixelY >> 1) * iStrideUV);
+    memcpy (pDstPic->pData[0], pSrcPic->pData[0], iHeightInPixelY * iStrideY);
+    memcpy (pDstPic->pData[1], pSrcPic->pData[1], (iHeightInPixelY >> 1) * iStrideUV);
+    memcpy (pDstPic->pData[2], pSrcPic->pData[2], (iHeightInPixelY >> 1) * iStrideUV);
   }
 }

@@ -119,6 +156,9 @@ void DoErrorConSliceCopy (PWelsDecoderContext pCtx) {
   PPicture pSrcPic = pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb;
   if ((pCtx->pParam->eEcActiveIdc == ERROR_CON_SLICE_COPY) && (pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag))
     pSrcPic = NULL; //no cross IDR method, should fill in data instead of copy
+  if (!IsEcRefPicCompatible (pCtx, pDstPic, pSrcPic)) {
+    pSrcPic = NULL;
+  }

   //uint8_t *pDstData[3], *pSrcData[3];
   bool* pMbCorrectlyDecodedFlag = pCtx->pCurDqLayer->pMbCorrectlyDecodedFlag;
diff --git a/test/decoder/DecUT_ErrorConcealment.cpp b/test/decoder/DecUT_ErrorConcealment.cpp
index 060bd60f..72d1d7b2 100644
--- a/test/decoder/DecUT_ErrorConcealment.cpp
+++ b/test/decoder/DecUT_ErrorConcealment.cpp
@@ -78,6 +78,8 @@ int32_t InitAndAllocInputData (PECInputCtx& pECCtx) {
   pECCtx->sWelsPic.iLinesize[0] = pECCtx->sAncPic.iLinesize[0] = pECCtx->sSrcPic.iLinesize[0] = pECCtx->iLinesize[0];
   pECCtx->sWelsPic.iLinesize[1] = pECCtx->sAncPic.iLinesize[1] = pECCtx->sSrcPic.iLinesize[1] = pECCtx->iLinesize[1];
   pECCtx->sWelsPic.iLinesize[2] = pECCtx->sAncPic.iLinesize[2] = pECCtx->sSrcPic.iLinesize[2] = pECCtx->iLinesize[2];
+  pECCtx->sWelsPic.iWidthInPixel = pECCtx->sAncPic.iWidthInPixel = pECCtx->sSrcPic.iWidthInPixel = pECCtx->iMbWidth << 4;
+  pECCtx->sWelsPic.iHeightInPixel = pECCtx->sAncPic.iHeightInPixel = pECCtx->sSrcPic.iHeightInPixel = pECCtx->iMbHeight << 4;

   pECCtx->pMbCorrectlyDecodedFlag = (bool*) WelsMallocz (pECCtx->iMbWidth * pECCtx->iMbHeight * sizeof (bool),
                                     "pECCtx->pMbCorrectlyDecodedFlag");
@@ -228,6 +230,23 @@ bool ComparePictureDataI420 (uint8_t* pSrcData, uint8_t* pDstData, const uint32_
   return bSame;
 }

+bool IsPictureFilledWithValueI420 (uint8_t* pData, const uint32_t kiStride, const int32_t kiHeight, const uint8_t kuiValue) {
+  const int32_t iLumaSize = kiStride * kiHeight;
+  const int32_t iChromaSize = (kiStride >> 1) * (kiHeight >> 1);
+
+  for (int32_t i = 0; i < iLumaSize; ++i) {
+    if (pData[i] != kuiValue) {
+      return false;
+    }
+  }
+  for (int32_t i = 0; i < iChromaSize; ++i) {
+    if (pData[iLumaSize + i] != kuiValue || pData[iLumaSize + iChromaSize + i] != kuiValue) {
+      return false;
+    }
+  }
+  return true;
+}
+
 //TEST cases followed
 TEST (ErrorConTest, DoErrorConFrameCopy) {
   bool bOK = true;
@@ -295,3 +314,52 @@ TEST (ErrorConTest, DoErrorConSliceCopy) {

   FreeInputData (pECCtx);
 }
+
+TEST (ErrorConTest, DoErrorConFrameCopyResolutionMismatchFallsBackToFill) {
+  PECInputCtx pECCtx = NULL;
+  if (InitAndAllocInputData (pECCtx)) {
+    FreeInputData (pECCtx);
+    return;
+  }
+
+  pECCtx->pCtx->pParam->eEcActiveIdc = ERROR_CON_FRAME_COPY_CROSS_IDR;
+  InitECCopyData (pECCtx);
+  pECCtx->pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag = 0;
+  pECCtx->pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb = &pECCtx->sSrcPic;
+
+  pECCtx->sSrcPic.iWidthInPixel -= 16;
+  memset (pECCtx->sWelsPic.pData[0], 7, pECCtx->iMbWidth * pECCtx->iMbHeight * 256 * 3 / 2);
+
+  DoErrorConFrameCopy (pECCtx->pCtx);
+
+  EXPECT_TRUE (IsPictureFilledWithValueI420 (pECCtx->sWelsPic.pData[0],
+               pECCtx->sWelsPic.iLinesize[0],
+               pECCtx->sWelsPic.iHeightInPixel,
+               128));
+  FreeInputData (pECCtx);
+}
+
+TEST (ErrorConTest, DoErrorConSliceCopyResolutionMismatchFallsBackToFill) {
+  PECInputCtx pECCtx = NULL;
+  if (InitAndAllocInputData (pECCtx)) {
+    FreeInputData (pECCtx);
+    return;
+  }
+
+  pECCtx->pCtx->pParam->eEcActiveIdc = ERROR_CON_SLICE_COPY_CROSS_IDR;
+  InitECCopyData (pECCtx);
+  pECCtx->pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt.bIdrFlag = 0;
+  pECCtx->pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb = &pECCtx->sSrcPic;
+
+  memset (pECCtx->pMbCorrectlyDecodedFlag, 0, pECCtx->iMbWidth * pECCtx->iMbHeight * sizeof (bool));
+  pECCtx->sSrcPic.iHeightInPixel -= 16;
+  memset (pECCtx->sWelsPic.pData[0], 7, pECCtx->iMbWidth * pECCtx->iMbHeight * 256 * 3 / 2);
+
+  DoErrorConSliceCopy (pECCtx->pCtx);
+
+  EXPECT_TRUE (IsPictureFilledWithValueI420 (pECCtx->sWelsPic.pData[0],
+               pECCtx->sWelsPic.iLinesize[0],
+               pECCtx->sWelsPic.iHeightInPixel,
+               128));
+  FreeInputData (pECCtx);
+}