Commit a72a8466 for openh264

commit a72a846670406f2a52a9e2643a013147f315a253
Author: BenzhengZhang <140143892+BenzhengZhang@users.noreply.github.com>
Date:   Fri Sep 4 16:01:20 2026 +0800

    Revert "decoder: avoid threaded post-return writes to caller ppDst (#4001)" (#4003)

    This reverts commit 5bfbf01cf85a00a4d02f3eecbbc268a116f940ce.

diff --git a/codec/decoder/core/inc/decoder_context.h b/codec/decoder/core/inc/decoder_context.h
index f1f83d25..4d9adebc 100644
--- a/codec/decoder/core/inc/decoder_context.h
+++ b/codec/decoder/core/inc/decoder_context.h
@@ -537,7 +537,6 @@ typedef struct tagSWelsDecThreadCtx {
   void* threadCtxOwner;
   uint8_t* kpSrc;
   int32_t kiSrcLen;
-  uint8_t* pDst[3];
   uint8_t** ppDst;
   SBufferInfo sDstInfo;
   PPicture pDec;
diff --git a/codec/decoder/plus/src/welsDecoderExt.cpp b/codec/decoder/plus/src/welsDecoderExt.cpp
index b879651d..73ead68d 100644
--- a/codec/decoder/plus/src/welsDecoderExt.cpp
+++ b/codec/decoder/plus/src/welsDecoderExt.cpp
@@ -321,7 +321,6 @@ void CWelsDecoder::OpenDecoderThreads() {
       m_pDecThrCtx[i].threadCtxOwner = this;
       m_pDecThrCtx[i].kpSrc = NULL;
       m_pDecThrCtx[i].kiSrcLen = 0;
-      m_pDecThrCtx[i].pDst[0] = m_pDecThrCtx[i].pDst[1] = m_pDecThrCtx[i].pDst[2] = NULL;
       m_pDecThrCtx[i].ppDst = NULL;
       m_pDecThrCtx[i].pDec = NULL;
       CREATE_EVENT (&m_pDecThrCtx[i].sImageReady, 1, 0, NULL);
@@ -1426,9 +1425,7 @@ int CWelsDecoder::ThreadDecodeFrameInternal (const unsigned char* kpSrc, const i
   }
   m_pDecThrCtx[signal].kpSrc = const_cast<uint8_t*> (kpSrc);
   m_pDecThrCtx[signal].kiSrcLen = kiSrcLen;
-  // Worker threads must not write into caller-owned ppDst after API returns.
-  m_pDecThrCtx[signal].pDst[0] = m_pDecThrCtx[signal].pDst[1] = m_pDecThrCtx[signal].pDst[2] = NULL;
-  m_pDecThrCtx[signal].ppDst = m_pDecThrCtx[signal].pDst;
+  m_pDecThrCtx[signal].ppDst = ppDst;
   memcpy (&m_pDecThrCtx[signal].sDstInfo, pDstInfo, sizeof (SBufferInfo));

   state = ParseAccessUnit (m_pDecThrCtx[signal]);
diff --git a/test/api/thread_decoder_test.cpp b/test/api/thread_decoder_test.cpp
index 4eddcff5..0f148fda 100644
--- a/test/api/thread_decoder_test.cpp
+++ b/test/api/thread_decoder_test.cpp
@@ -7,11 +7,6 @@
 #include <string>
 #include <vector>

-#if !defined(_WIN32)
-#include <sys/mman.h>
-#include <unistd.h>
-#endif
-
 static void UpdateHashFromPlane (SHA1Context* ctx, const uint8_t* plane,
                                  int width, int height, int stride) {
   for (int i = 0; i < height; i++) {
@@ -476,81 +471,3 @@ TEST_F (ThreadDecoderReorderQueueRaceTest, BufferedPictureQueueDrainsAllFrames)
   ASSERT_FALSE (HasFatalFailure());
   EXPECT_EQ (iDecodedFrames_, 50);
 }
-
-// Regression coverage for the threaded ppDst post-return write.
-// In threaded decode, DecodeFrameNoDelay() hands the caller-owned ppDst
-// pointer array to a worker thread; decoding continues asynchronously and can
-// write into ppDst after the API call has already returned. If the caller's
-// storage is short-lived (e.g. freed or unmapped right after the call), that
-// late write is a use-after-free. The worker now writes into thread-owned
-// storage only, so the caller's page can be unmapped immediately after each
-// call without a crash.
-//
-// Iterations are capped: a separate, already-tracked AU-list counter overflow
-// in ResetCurrentAccessUnit surfaces around iteration 13 on this input
-// regardless of this fix, so this test stays inside that bound to isolate the
-// ppDst behavior under test.
-TEST (ThreadDecoderSecurityTest, DecodeFrameNoDelayNoPostReturnWriteToCallerPpDst) {
-#if defined(_WIN32)
-  GTEST_SKIP() << "mmap/munmap based lifetime stress is POSIX-only";
-#else
-  ISVCDecoder* decoder = NULL;
-  ASSERT_EQ (0, WelsCreateDecoder (&decoder));
-  ASSERT_TRUE (decoder != NULL);
-
-  SDecodingParam decParam;
-  memset (&decParam, 0, sizeof (decParam));
-  decParam.uiTargetDqLayer = UCHAR_MAX;
-  decParam.eEcActiveIdc = ERROR_CON_SLICE_COPY;
-  decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
-  int32_t iThreadCount = 2;
-  decoder->SetOption (DECODER_OPTION_NUM_OF_THREADS, &iThreadCount);
-  ASSERT_EQ (0, decoder->Initialize (&decParam));
-
-  std::ifstream file ("res/BA_MW_D.264", std::ios::in | std::ios::binary);
-  ASSERT_TRUE (file.is_open());
-  std::vector<uint8_t> bitstream ((std::istreambuf_iterator<char> (file)), std::istreambuf_iterator<char>());
-  ASSERT_FALSE (bitstream.empty());
-
-  const size_t pageSize = static_cast<size_t> (sysconf (_SC_PAGESIZE));
-  const size_t chunkSize = 1200;
-  const int32_t kMaxIters = 12;
-
-  for (size_t off = 0, iter = 0; off < bitstream.size() && iter < static_cast<size_t> (kMaxIters);
-       off += chunkSize, ++iter) {
-    void* page = mmap (NULL, pageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-    ASSERT_NE (MAP_FAILED, page);
-
-    unsigned char** ppDst = reinterpret_cast<unsigned char**> (page);
-    memset (ppDst, 0, pageSize);
-
-    SBufferInfo dstInfo;
-    memset (&dstInfo, 0, sizeof (dstInfo));
-    int32_t len = static_cast<int32_t> (std::min (chunkSize, bitstream.size() - off));
-    DECODING_STATE rv = decoder->DecodeFrameNoDelay (bitstream.data() + off, len, ppDst, &dstInfo);
-    // The 1200-byte offsets are not NAL/AU aligned, so bitstream-parsing-level
-    // states (dsBitstreamError, dsDataErrorConcealed, ...) are expected with
-    // ERROR_CON_SLICE_COPY; only logic-level failures indicate a real problem.
-    EXPECT_EQ (0, rv & (dsInvalidArgument | dsInitialOptExpected | dsOutOfMemory));
-
-    munmap (page, pageSize);
-    usleep (5000);
-  }
-
-  // Drain pipelined in-flight frames before teardown so Uninitialize() does not
-  // free decoder state while a worker thread is still reconstructing.
-  uint8_t* dst[3] = {NULL, NULL, NULL};
-  SBufferInfo info;
-  int32_t endOfStream = 1;
-  decoder->SetOption (DECODER_OPTION_END_OF_STREAM, &endOfStream);
-  int32_t remaining = 0;
-  decoder->GetOption (DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER, &remaining);
-  for (int32_t i = 0; i < remaining; ++i) {
-    memset (&info, 0, sizeof (info));
-    decoder->FlushFrame (dst, &info);
-  }
-
-  decoder->Uninitialize();
-  WelsDestroyDecoder (decoder);
-#endif
-}