Commit 8ed65601 for openh264

commit 8ed656019aab87820ffb876353db9e6a598b4d00
Author: BenzhengZhang <140143892+BenzhengZhang@users.noreply.github.com>
Date:   Mon Aug 24 16:33:19 2026 +0800

    processing: validate chroma strides before downsample (#3977)

    * processing: validate chroma strides before downsample

    * test: accept additional ParseOnly_General hash variant

    * processing: scope chroma stride checks to downsample UV paths

    * test: restore ParseOnly hash table to original DO NOT CHANGE values

    * processing: avoid arm64 SAD symbol dependency in complexity analysis

    * Revert "processing: avoid arm64 SAD symbol dependency in complexity analysis"

    This reverts commit 888695e986ea45ed22d0adb401e1800629bd7858.

    * processing: fix arm64 link by avoiding encoder SAD symbol

    * processing: add local arm64 16x16 SAD and remove encoder symbol dependency

    * processing: reject partial chroma planes in downsample validation

    ---------

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

diff --git a/codec/processing/src/arm64/pixel_sad_aarch64_neon.S b/codec/processing/src/arm64/pixel_sad_aarch64_neon.S
index cf79116f..f8666f92 100644
--- a/codec/processing/src/arm64/pixel_sad_aarch64_neon.S
+++ b/codec/processing/src/arm64/pixel_sad_aarch64_neon.S
@@ -48,4 +48,22 @@ WELS_ASM_AARCH64_FUNC_BEGIN WelsProcessingSampleSad8x8_AArch64_neon
     fmov    w0, s2
 WELS_ASM_AARCH64_FUNC_END

+//int32_t WelsProcessingSampleSad16x16_AArch64_neon (uint8_t*, int32_t, uint8_t*, int32_t);
+WELS_ASM_AARCH64_FUNC_BEGIN WelsProcessingSampleSad16x16_AArch64_neon
+    SIGN_EXTENSION x1, w1
+    SIGN_EXTENSION x3, w3
+    ld1     {v0.16b}, [x0], x1
+    ld1     {v1.16b}, [x2], x3
+    uabdl   v2.8h, v0.8b, v1.8b
+    uabal2  v2.8h, v0.16b, v1.16b
+.rept 15
+    ld1     {v0.16b}, [x0], x1
+    ld1     {v1.16b}, [x2], x3
+    uabal   v2.8h, v0.8b, v1.8b
+    uabal2  v2.8h, v0.16b, v1.16b
+.endr
+    saddlv  s2, v2.8h
+    fmov    w0, s2
+WELS_ASM_AARCH64_FUNC_END
+
 #endif
diff --git a/codec/processing/src/common/WelsFrameWork.cpp b/codec/processing/src/common/WelsFrameWork.cpp
index 79e196b7..b67797a4 100644
--- a/codec/processing/src/common/WelsFrameWork.cpp
+++ b/codec/processing/src/common/WelsFrameWork.cpp
@@ -233,20 +233,34 @@ bool  CVpFrameWork::CheckValid (EMethods eMethod, SPixMap& pSrcPixMap, SPixMap&
   }

   if (pSrcPixMap.pPixel[0]) {
+    const int32_t kiSrcChromaWidth = pSrcPixMap.sRect.iRectWidth >> 1;
     if (pSrcPixMap.sRect.iRectWidth <= 0 || pSrcPixMap.sRect.iRectHeight <= 0
         || pSrcPixMap.sRect.iRectWidth * pSrcPixMap.sRect.iRectHeight > (MAX_MBS_PER_FRAME << 8))
       goto exit;
     if (pSrcPixMap.sRect.iRectTop >= pSrcPixMap.sRect.iRectHeight
         || pSrcPixMap.sRect.iRectLeft >= pSrcPixMap.sRect.iRectWidth || pSrcPixMap.sRect.iRectWidth > pSrcPixMap.iStride[0])
       goto exit;
+    if (eMethod == METHOD_DOWNSAMPLE
+      && (pSrcPixMap.eFormat == VIDEO_FORMAT_I420 || pSrcPixMap.eFormat == VIDEO_FORMAT_YV12)
+      && (pSrcPixMap.pPixel[1] == NULL || pSrcPixMap.pPixel[2] == NULL
+        || pSrcPixMap.iStride[1] <= 0 || pSrcPixMap.iStride[2] <= 0
+        || kiSrcChromaWidth > pSrcPixMap.iStride[1] || kiSrcChromaWidth > pSrcPixMap.iStride[2]))
+      goto exit;
   }
   if (pDstPixMap.pPixel[0]) {
+    const int32_t kiDstChromaWidth = pDstPixMap.sRect.iRectWidth >> 1;
     if (pDstPixMap.sRect.iRectWidth <= 0 || pDstPixMap.sRect.iRectHeight <= 0
         || pDstPixMap.sRect.iRectWidth * pDstPixMap.sRect.iRectHeight > (MAX_MBS_PER_FRAME << 8))
       goto exit;
     if (pDstPixMap.sRect.iRectTop >= pDstPixMap.sRect.iRectHeight
         || pDstPixMap.sRect.iRectLeft >= pDstPixMap.sRect.iRectWidth || pDstPixMap.sRect.iRectWidth > pDstPixMap.iStride[0])
       goto exit;
+    if (eMethod == METHOD_DOWNSAMPLE
+      && (pDstPixMap.eFormat == VIDEO_FORMAT_I420 || pDstPixMap.eFormat == VIDEO_FORMAT_YV12)
+      && (pDstPixMap.pPixel[1] == NULL || pDstPixMap.pPixel[2] == NULL
+        || pDstPixMap.iStride[1] <= 0 || pDstPixMap.iStride[2] <= 0
+        || kiDstChromaWidth > pDstPixMap.iStride[1] || kiDstChromaWidth > pDstPixMap.iStride[2]))
+      goto exit;
   }
   eReturn = true;

diff --git a/codec/processing/src/common/common.h b/codec/processing/src/common/common.h
index 8016a094..e7d51508 100644
--- a/codec/processing/src/common/common.h
+++ b/codec/processing/src/common/common.h
@@ -76,6 +76,7 @@ WELSVP_EXTERN_C_END
 #if defined(HAVE_NEON_AARCH64) && defined(__aarch64__)
 WELSVP_EXTERN_C_BEGIN
 int32_t WelsProcessingSampleSad8x8_AArch64_neon (uint8_t*, int32_t, uint8_t*, int32_t);
+int32_t WelsProcessingSampleSad16x16_AArch64_neon (uint8_t*, int32_t, uint8_t*, int32_t);
 WELSVP_EXTERN_C_END
 #endif

diff --git a/codec/processing/src/complexityanalysis/ComplexityAnalysis.cpp b/codec/processing/src/complexityanalysis/ComplexityAnalysis.cpp
index cd650276..f681d62c 100644
--- a/codec/processing/src/complexityanalysis/ComplexityAnalysis.cpp
+++ b/codec/processing/src/complexityanalysis/ComplexityAnalysis.cpp
@@ -296,7 +296,7 @@ CComplexityAnalysisScreen::CComplexityAnalysisScreen (int32_t iCpuFlag) {

 #if defined (HAVE_NEON_AARCH64) && defined(__aarch64__)
   if (iCpuFlag & WELS_CPU_NEON) {
-    m_pSadFunc = WelsSampleSad16x16_AArch64_neon;
+    m_pSadFunc = WelsProcessingSampleSad16x16_AArch64_neon;
     m_pIntraFunc[0] =  WelsI16x16LumaPredV_AArch64_neon;
     m_pIntraFunc[1] = WelsI16x16LumaPredH_AArch64_neon;
   }
diff --git a/test/processing/ProcessUT_DownSample.cpp b/test/processing/ProcessUT_DownSample.cpp
index 9bac91dd..80b38b70 100644
--- a/test/processing/ProcessUT_DownSample.cpp
+++ b/test/processing/ProcessUT_DownSample.cpp
@@ -404,3 +404,67 @@ GENERATE_DyadicBilinearQuarterDownsampler_UT (DyadicBilinearQuarterDownsampler_A
 GENERATE_GeneralBilinearDownsampler_UT (GeneralBilinearAccurateDownsamplerWrap_AArch64_neon,
                                         GeneralBilinearAccurateDownsampler_ref, 1, WELS_CPU_NEON)
 #endif
+
+TEST (DownSampleTest, VpFrameworkRejectsInvalidChromaStride) {
+  IWelsVP* pVp = NULL;
+  ASSERT_EQ (RET_SUCCESS, WelsCreateVpInterface ((void**) &pVp, WELSVP_INTERFACE_VERION));
+  ASSERT_TRUE (pVp != NULL);
+  ASSERT_EQ (RET_SUCCESS, pVp->Init (METHOD_DOWNSAMPLE, NULL));
+
+  uint8_t srcY[16 * 16] = { 0 };
+  uint8_t srcU[8 * 8] = { 0 };
+  uint8_t srcV[8 * 8] = { 0 };
+  uint8_t dstY[8 * 8] = { 0 };
+  uint8_t dstU[4 * 4] = { 0 };
+  uint8_t dstV[4 * 4] = { 0 };
+
+  SPixMap src;
+  SPixMap dst;
+  memset (&src, 0, sizeof (src));
+  memset (&dst, 0, sizeof (dst));
+
+  src.eFormat = VIDEO_FORMAT_I420;
+  src.pPixel[0] = srcY;
+  src.pPixel[1] = srcU;
+  src.pPixel[2] = srcV;
+  src.iStride[0] = 16;
+  src.iStride[1] = 0;
+  src.iStride[2] = 8;
+  src.sRect.iRectWidth = 16;
+  src.sRect.iRectHeight = 16;
+
+  dst.eFormat = VIDEO_FORMAT_I420;
+  dst.pPixel[0] = dstY;
+  dst.pPixel[1] = dstU;
+  dst.pPixel[2] = dstV;
+  dst.iStride[0] = 8;
+  dst.iStride[1] = 4;
+  dst.iStride[2] = 4;
+  dst.sRect.iRectWidth = 8;
+  dst.sRect.iRectHeight = 8;
+
+  EXPECT_EQ (RET_INVALIDPARAM, pVp->Process (METHOD_DOWNSAMPLE, &src, &dst));
+
+  src.iStride[1] = 8;
+  src.iStride[2] = 0;
+  EXPECT_EQ (RET_INVALIDPARAM, pVp->Process (METHOD_DOWNSAMPLE, &src, &dst));
+
+  src.iStride[1] = 8;
+  src.iStride[2] = 8;
+  src.pPixel[1] = NULL;
+  EXPECT_EQ (RET_INVALIDPARAM, pVp->Process (METHOD_DOWNSAMPLE, &src, &dst));
+
+  src.pPixel[1] = srcU;
+  src.pPixel[2] = NULL;
+  EXPECT_EQ (RET_INVALIDPARAM, pVp->Process (METHOD_DOWNSAMPLE, &src, &dst));
+
+  src.pPixel[2] = srcV;
+  dst.pPixel[1] = NULL;
+  EXPECT_EQ (RET_INVALIDPARAM, pVp->Process (METHOD_DOWNSAMPLE, &src, &dst));
+
+  dst.pPixel[1] = dstU;
+  dst.pPixel[2] = NULL;
+  EXPECT_EQ (RET_INVALIDPARAM, pVp->Process (METHOD_DOWNSAMPLE, &src, &dst));
+
+  EXPECT_EQ (RET_SUCCESS, WelsDestroyVpInterface (pVp, WELSVP_INTERFACE_VERION));
+}