[RESTRICT AUTOMERGE] CTS test for Android security b/143464314

Bug: 143464314
Bug: 163560253
Test: Ran the new testcase on android-10.0.0_r1 with/without patch

Change-Id: I4071f0725e76c4d7808c05226935676520d1221f
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0213.hevc b/hostsidetests/securitybulletin/res/cve_2020_0213.hevc
new file mode 100644
index 0000000..f34f874
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0213.hevc
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0213_info.txt b/hostsidetests/securitybulletin/res/cve_2020_0213_info.txt
new file mode 100644
index 0000000..0dde4a8
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2020_0213_info.txt
@@ -0,0 +1,2 @@
+73 32 0
+304 0 33333
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/Android.bp
new file mode 100644
index 0000000..1a07265
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/Android.bp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+cc_test {
+    name: "CVE-2020-0213",
+    defaults: [
+        "cts_hostsidetests_securitybulletin_defaults",
+        "libcodec2-hidl-client-defaults",
+    ],
+    srcs: [
+        "poc.cpp",
+    ],
+    shared_libs: [
+        "libcodec2_client",
+    ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/poc.cpp
new file mode 100644
index 0000000..09e9529
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/poc.cpp
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <C2AllocatorIon.h>
+#include <C2Buffer.h>
+#include <C2BufferPriv.h>
+#include <C2Config.h>
+#include <C2Debug.h>
+#include <codec2/hidl/client.h>
+#include <gui/BufferQueue.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IProducerListener.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <fstream>
+#include <iostream>
+
+#include "../includes/common.h"
+
+using android::C2AllocatorIon;
+using std::chrono_literals::operator""ms;
+
+#define MAXIMUM_NUMBER_OF_RETRIES 20
+#define QUEUE_TIMEOUT 400ms
+#define MAXIMUM_NUMBER_OF_INPUT_BUFFERS 8
+#define ALIGN(_sz, _align) ((_sz + (_align - 1)) & ~(_align - 1))
+
+void workDone(
+    const std::shared_ptr<android::Codec2Client::Component>& component,
+    std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
+    std::mutex& queueLock, std::condition_variable& queueCondition,
+    std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
+    uint32_t& framesReceived);
+
+struct FrameInfo {
+  int bytesCount;
+  uint32_t flags;
+  int64_t timestamp;
+};
+
+class LinearBuffer : public C2Buffer {
+ public:
+  explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
+      : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
+
+  explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block,
+                        size_t size)
+      : C2Buffer({block->share(block->offset(), size, ::C2Fence())}) {}
+};
+
+/*
+ * Handle Callback functions onWorkDone(), onTripped(),
+ * onError(), onDeath(), onFramesRendered()
+ */
+struct CodecListener : public android::Codec2Client::Listener {
+ public:
+  CodecListener(
+      const std::function<void(std::list<std::unique_ptr<C2Work>>& workItems)>
+          fn = nullptr)
+      : callBack(fn) {}
+  virtual void onWorkDone(
+      const std::weak_ptr<android::Codec2Client::Component>& comp,
+      std::list<std::unique_ptr<C2Work>>& workItems) override {
+    (void)comp;
+    if (callBack) {
+      callBack(workItems);
+    }
+  }
+
+  virtual void onTripped(
+      const std::weak_ptr<android::Codec2Client::Component>& comp,
+      const std::vector<std::shared_ptr<C2SettingResult>>& settingResults)
+      override {
+    (void)comp;
+    (void)settingResults;
+  }
+
+  virtual void onError(
+      const std::weak_ptr<android::Codec2Client::Component>& comp,
+      uint32_t errorCode) override {
+    (void)comp;
+    (void)errorCode;
+  }
+
+  virtual void onDeath(
+      const std::weak_ptr<android::Codec2Client::Component>& comp) override {
+    (void)comp;
+  }
+
+  virtual void onInputBufferDone(uint64_t frameIndex,
+                                 size_t arrayIndex) override {
+    (void)frameIndex;
+    (void)arrayIndex;
+  }
+
+  virtual void onFrameRendered(uint64_t bufferQueueId, int32_t slotId,
+                               int64_t timestampNs) override {
+    (void)bufferQueueId;
+    (void)slotId;
+    (void)timestampNs;
+  }
+  std::function<void(std::list<std::unique_ptr<C2Work>>& workItems)> callBack;
+};
+
+class Codec2VideoDecHidlTestBase {
+ public:
+  bool SetUp() {
+    mClient = getClient();
+    if (!mClient) {
+      return false;
+    }
+    mListener.reset(new CodecListener(
+        [this](std::list<std::unique_ptr<C2Work>>& workItems) {
+          handleWorkDone(workItems);
+        }));
+    if (!mListener) {
+      return false;
+    }
+    mComponent = android::Codec2Client::CreateComponentByName(
+        mComponentName.c_str(), mListener, &mClient);
+    if (!mComponent) {
+      return false;
+    }
+    for (int i = 0; i < MAXIMUM_NUMBER_OF_INPUT_BUFFERS; ++i) {
+      mWorkQueue.emplace_back(new C2Work);
+    }
+    std::shared_ptr<C2AllocatorStore> store =
+        android::GetCodec2PlatformAllocatorStore();
+    if (store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR,
+                              &mLinearAllocator) != C2_OK) {
+      return false;
+    }
+    mLinearPool =
+        std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
+    if (!mLinearPool) {
+      return false;
+    }
+    mEos = false;
+    mHasVulnerability = false;
+    mTimestampUs = 0u;
+    mWorkResult = C2_OK;
+    mFramesReceived = 0;
+    return true;
+  }
+
+  ~Codec2VideoDecHidlTestBase() {
+    if (mComponent != nullptr) {
+      mComponent->release();
+      mComponent = nullptr;
+    }
+  }
+
+  std::shared_ptr<android::Codec2Client> getClient() {
+    auto instances = android::Codec2Client::GetServiceNames();
+    for (std::string instance : instances) {
+      std::shared_ptr<android::Codec2Client> client =
+          android::Codec2Client::CreateFromService(instance.c_str());
+      std::vector<C2Component::Traits> components = client->listComponents();
+      for (C2Component::Traits traits : components) {
+        if (instance.compare(traits.owner)) {
+          continue;
+        }
+        if (traits.domain == DOMAIN_VIDEO && traits.kind == KIND_DECODER &&
+            mComponentName.compare(traits.name)) {
+          return android::Codec2Client::CreateFromService(instance.c_str());
+        }
+      }
+    }
+    return nullptr;
+  }
+
+  void checkBufferOK(std::unique_ptr<C2Work>& work) {
+    const C2GraphicView output = work->worklets.front()
+                                     ->output.buffers[0]
+                                     ->data()
+                                     .graphicBlocks()
+                                     .front()
+                                     .map()
+                                     .get();
+    uint8_t* uPlane =
+        const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_U]);
+    uint8_t* vPlane =
+        const_cast<uint8_t*>(output.data()[C2PlanarLayout::PLANE_V]);
+    const uint8_t ul[] = {109, 109, 109, 109, 109, 109, 109};
+    const uint8_t vl[] = {121, 121, 121, 121, 121, 121, 121};
+    const uint8_t ur[] = {114, 114, 120, 120, 122, 127, 127};
+    const uint8_t vr[] = {126, 121, 123, 121, 123, 126, 126};
+    if (!memcmp(uPlane - 7, ul, 7) && !memcmp(vPlane - 7, vl, 7) &&
+        !memcmp(uPlane + 1, ur, 7) && !memcmp(vPlane + 1, vr, 7)) {
+      mHasVulnerability |= true;
+    }
+  }
+
+  // Config output pixel format
+  bool configPixelFormat(uint32_t format) {
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    C2StreamPixelFormatInfo::output pixelformat(0u, format);
+
+    std::vector<C2Param*> configParam{&pixelformat};
+    c2_status_t status =
+        mComponent->config(configParam, C2_DONT_BLOCK, &failures);
+    if (status == C2_OK && failures.size() == 0u) {
+      return true;
+    }
+    return false;
+  }
+
+  // callback function to process onWorkDone received by Listener
+  void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
+    for (std::unique_ptr<C2Work>& work : workItems) {
+      if (!work->worklets.empty()) {
+        // For decoder components current timestamp always exceeds
+        // previous timestamp if output is in display order
+        mWorkResult |= work->result;
+        bool codecConfig = ((work->worklets.front()->output.flags &
+                             C2FrameData::FLAG_CODEC_CONFIG) != 0);
+        if (!codecConfig && !work->worklets.front()->output.buffers.empty()) {
+          checkBufferOK(work);
+        }
+        bool mCsd = false;
+        workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition,
+                 mWorkQueue, mEos, mCsd, mFramesReceived);
+        (void)mCsd;
+      }
+    }
+  }
+
+  const std::string mComponentName = "c2.android.hevc.decoder";
+  bool mEos;
+  bool mHasVulnerability;
+  uint64_t mTimestampUs;
+  int32_t mWorkResult;
+  uint32_t mFramesReceived;
+  std::list<uint64_t> mFlushedIndices;
+
+  C2BlockPool::local_id_t mBlockPoolId;
+  std::shared_ptr<C2BlockPool> mLinearPool;
+  std::shared_ptr<C2Allocator> mLinearAllocator;
+
+  std::mutex mQueueLock;
+  std::condition_variable mQueueCondition;
+  std::list<std::unique_ptr<C2Work>> mWorkQueue;
+
+  std::shared_ptr<android::Codec2Client> mClient;
+  std::shared_ptr<android::Codec2Client::Listener> mListener;
+  std::shared_ptr<android::Codec2Client::Component> mComponent;
+};
+
+// process onWorkDone received by Listener
+void workDone(
+    const std::shared_ptr<android::Codec2Client::Component>& component,
+    std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
+    std::mutex& queueLock, std::condition_variable& queueCondition,
+    std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
+    uint32_t& framesReceived) {
+  // handle configuration changes in work done
+  if (work->worklets.front()->output.configUpdate.size() != 0) {
+    std::vector<std::unique_ptr<C2Param>> updates =
+        std::move(work->worklets.front()->output.configUpdate);
+    std::vector<C2Param*> configParam;
+    std::vector<std::unique_ptr<C2SettingResult>> failures;
+    for (size_t i = 0; i < updates.size(); ++i) {
+      C2Param* param = updates[i].get();
+      if (param->index() == C2StreamInitDataInfo::output::PARAM_TYPE) {
+        C2StreamInitDataInfo::output* csdBuffer =
+            (C2StreamInitDataInfo::output*)(param);
+        size_t csdSize = csdBuffer->flexCount();
+        if (csdSize > 0) {
+          csd = true;
+        }
+      } else if ((param->index() ==
+                  C2StreamSampleRateInfo::output::PARAM_TYPE) ||
+                 (param->index() ==
+                  C2StreamChannelCountInfo::output::PARAM_TYPE) ||
+                 (param->index() ==
+                  C2StreamPictureSizeInfo::output::PARAM_TYPE)) {
+        configParam.push_back(param);
+      }
+    }
+    component->config(configParam, C2_DONT_BLOCK, &failures);
+    assert(failures.size() == 0u);
+  }
+  if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
+    ++framesReceived;
+    eos = (work->worklets.front()->output.flags &
+           C2FrameData::FLAG_END_OF_STREAM) != 0;
+    auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(),
+                                  work->input.ordinal.frameIndex.peeku());
+    work->input.buffers.clear();
+    work->worklets.clear();
+    {
+      typedef std::unique_lock<std::mutex> ULock;
+      ULock l(queueLock);
+      workQueue.push_back(std::move(work));
+      if (!flushedIndices.empty() && (frameIndexIt != flushedIndices.end())) {
+        flushedIndices.erase(frameIndexIt);
+      }
+      queueCondition.notify_all();
+    }
+  }
+}
+
+bool decodeNFrames(
+    const std::shared_ptr<android::Codec2Client::Component>& component,
+    std::mutex& queueLock, std::condition_variable& queueCondition,
+    std::list<std::unique_ptr<C2Work>>& workQueue,
+    std::list<uint64_t>& flushedIndices,
+    std::shared_ptr<C2BlockPool>& linearPool, std::ifstream& ifStream,
+    android::Vector<FrameInfo>* Info) {
+  typedef std::unique_lock<std::mutex> ULock;
+  int frameID = 0;
+  int retryCount = 0;
+  while (1) {
+    if (frameID == (int)Info->size()) {
+      break;
+    }
+    uint32_t flags = 0;
+    std::unique_ptr<C2Work> work;
+    // Prepare C2Work
+    while (!work && (retryCount < MAXIMUM_NUMBER_OF_RETRIES)) {
+      ULock l(queueLock);
+      if (!workQueue.empty()) {
+        work.swap(workQueue.front());
+        workQueue.pop_front();
+      } else {
+        queueCondition.wait_for(l, QUEUE_TIMEOUT);
+        ++retryCount;
+      }
+    }
+    if (!work && (retryCount >= MAXIMUM_NUMBER_OF_RETRIES)) {
+      return false;  // "Wait for generating C2Work exceeded timeout"
+    }
+    int64_t timestamp = (*Info)[frameID].timestamp;
+    if ((*Info)[frameID].flags) {
+      flags = (1 << ((*Info)[frameID].flags - 1));
+    }
+    if (frameID == (int)Info->size() - 1) {
+      flags |= C2FrameData::FLAG_END_OF_STREAM;
+    }
+
+    work->input.flags = (C2FrameData::flags_t)flags;
+    work->input.ordinal.timestamp = timestamp;
+    work->input.ordinal.frameIndex = frameID;
+    {
+      ULock l(queueLock);
+      flushedIndices.emplace_back(frameID);
+    }
+
+    int size = (*Info)[frameID].bytesCount;
+    char* data = (char*)malloc(size);
+    if (!data) {
+      return false;
+    }
+
+    ifStream.read(data, size);
+    if (ifStream.gcount() != size) {
+      return false;
+    }
+
+    work->input.buffers.clear();
+    auto alignedSize = ALIGN(size, PAGE_SIZE);
+    if (size) {
+      std::shared_ptr<C2LinearBlock> block;
+      if (linearPool->fetchLinearBlock(
+              alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE},
+              &block) != C2_OK) {
+        return false;
+      }
+      if (!block) {
+        return false;
+      }
+
+      // Write View
+      C2WriteView view = block->map().get();
+      if (view.error() != C2_OK) {
+        return false;
+      }
+      if ((size_t)alignedSize != view.capacity()) {
+        return false;
+      }
+      if (0u != view.offset()) {
+        return false;
+      }
+      if ((size_t)alignedSize != view.size()) {
+        return false;
+      }
+
+      memcpy(view.base(), data, size);
+
+      work->input.buffers.emplace_back(new LinearBuffer(block, size));
+      free(data);
+    }
+    work->worklets.clear();
+    work->worklets.emplace_back(new C2Worklet);
+    std::list<std::unique_ptr<C2Work>> items;
+    items.push_back(std::move(work));
+
+    // DO THE DECODING
+    if (component->queue(&items) != C2_OK) {
+      return false;
+    }
+    ++frameID;
+    retryCount = 0;
+  }
+  return true;
+}
+
+// Wait for all the inputs to be consumed by the plugin.
+void waitOnInputConsumption(
+    std::mutex& queueLock, std::condition_variable& queueCondition,
+    std::list<std::unique_ptr<C2Work>>& workQueue,
+    size_t bufferCount = MAXIMUM_NUMBER_OF_INPUT_BUFFERS) {
+  typedef std::unique_lock<std::mutex> ULock;
+  uint32_t queueSize;
+  uint32_t retryCount = 0;
+  {
+    ULock l(queueLock);
+    queueSize = workQueue.size();
+  }
+  while ((retryCount < MAXIMUM_NUMBER_OF_RETRIES) &&
+         (queueSize < bufferCount)) {
+    ULock l(queueLock);
+    if (queueSize != workQueue.size()) {
+      queueSize = workQueue.size();
+      retryCount = 0;
+    } else {
+      queueCondition.wait_for(l, QUEUE_TIMEOUT);
+      ++retryCount;
+    }
+  }
+}
+
+// Populate Info vector and return number of CSDs
+int32_t populateInfoVector(std::string info,
+                           android::Vector<FrameInfo>* frameInfo) {
+  std::ifstream eleInfo;
+  eleInfo.open(info);
+  if (!eleInfo.is_open()) {
+    return -1;
+  }
+  int32_t numCsds = 0;
+  int32_t bytesCount = 0;
+  uint32_t flags = 0;
+  uint32_t timestamp = 0;
+  while (1) {
+    if (!(eleInfo >> bytesCount)) {
+      break;
+    }
+    eleInfo >> flags;
+    eleInfo >> timestamp;
+    bool codecConfig =
+        flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
+    if (codecConfig) {
+      ++numCsds;
+    }
+    frameInfo->push_back({bytesCount, flags, timestamp});
+  }
+  eleInfo.close();
+  return numCsds;
+}
+
+#define RETURN_FAILURE(condition) \
+  if ((condition)) {              \
+    return EXIT_FAILURE;          \
+  }
+
+int main(int argc, char** argv) {
+  RETURN_FAILURE(argc != 3);
+
+  Codec2VideoDecHidlTestBase handle;
+  RETURN_FAILURE(!handle.SetUp());
+  RETURN_FAILURE(!handle.configPixelFormat(HAL_PIXEL_FORMAT_YCBCR_420_888));
+
+  std::string eleStreamInfo{argv[2]};
+  android::Vector<FrameInfo> Info;
+  RETURN_FAILURE(populateInfoVector(eleStreamInfo, &Info) < 0);
+  RETURN_FAILURE(handle.mComponent->start() != C2_OK);
+
+  std::string eleStream{argv[1]};
+  std::ifstream ifStream;
+  ifStream.open(eleStream, std::ifstream::binary);
+  RETURN_FAILURE(!ifStream.is_open());
+  RETURN_FAILURE(!decodeNFrames(handle.mComponent, handle.mQueueLock,
+                                handle.mQueueCondition, handle.mWorkQueue,
+                                handle.mFlushedIndices, handle.mLinearPool,
+                                ifStream, &Info));
+  // blocking call to ensures application to Wait till all the inputs are
+  // consumed
+  if (!handle.mEos) {
+    waitOnInputConsumption(handle.mQueueLock, handle.mQueueCondition,
+                           handle.mWorkQueue);
+  }
+  ifStream.close();
+  RETURN_FAILURE(handle.mFramesReceived != Info.size());
+  RETURN_FAILURE(handle.mComponent->stop() != C2_OK);
+  RETURN_FAILURE(handle.mWorkResult != C2_OK);
+  if (handle.mHasVulnerability) {
+    return EXIT_VULNERABLE;
+  }
+  return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
index 640a02b..83e573e 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
@@ -518,6 +518,19 @@
      ******************************************************************************/
 
     /**
+     * b/143464314
+     * Vulnerability Behaviour: SIGSEGV in self / EXIT_VULNERABLE (113)
+     */
+    @SecurityTest(minPatchLevel = "2020-10")
+    @Test
+    public void testPocCVE_2020_0213() throws Exception {
+        String inputFiles[] = {"cve_2020_0213.hevc", "cve_2020_0213_info.txt"};
+        AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0213",
+            AdbUtils.TMP_PATH + inputFiles[0] + " " + AdbUtils.TMP_PATH + inputFiles[1],
+            inputFiles, AdbUtils.TMP_PATH, getDevice());
+    }
+
+    /**
      * b/166268541
      * Vulnerability Behaviour: SIGSEGV in media.swcodec
      */