diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2020-11-19 23:58:18 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2020-11-19 23:58:18 +0000 |
commit | cbebc04306c4536cf971eb93b782ff272e6a3ced (patch) | |
tree | 56dda556eed6d346f8f48195f2edfb73c7220466 | |
parent | 10051718486ea6fcd8507a8fb3482d3c321dbae9 (diff) | |
parent | b21d2c6e3f616aa28b3e289476fe680a0b357922 (diff) | |
download | cts-cbebc04306c4536cf971eb93b782ff272e6a3ced.tar.gz |
Merge changes I4071f072,I535d72f3 into rvc-dev
* changes:
[RESTRICT AUTOMERGE] CTS test for Android security b/143464314
[RESTRICT AUTOMERGE] CTS test for Android security b/166268541
-rw-r--r-- | hostsidetests/securitybulletin/res/cve_2020_0213.hevc | bin | 0 -> 377 bytes | |||
-rw-r--r-- | hostsidetests/securitybulletin/res/cve_2020_0213_info.txt | 2 | ||||
-rw-r--r-- | hostsidetests/securitybulletin/res/cve_2020_0470.mp4 | bin | 0 -> 72 bytes | |||
-rw-r--r-- | hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/Android.bp | 30 | ||||
-rw-r--r-- | hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/poc.cpp | 482 | ||||
-rw-r--r-- | hostsidetests/securitybulletin/securityPatch/CVE-2020-0470/Android.bp | 35 | ||||
-rw-r--r-- | hostsidetests/securitybulletin/securityPatch/CVE-2020-0470/poc.cpp | 131 | ||||
-rw-r--r-- | hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java | 27 |
8 files changed, 707 insertions, 0 deletions
diff --git a/hostsidetests/securitybulletin/res/cve_2020_0213.hevc b/hostsidetests/securitybulletin/res/cve_2020_0213.hevc Binary files differnew file mode 100644 index 00000000000..f34f874aeae --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2020_0213.hevc 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 00000000000..0dde4a859f1 --- /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/res/cve_2020_0470.mp4 b/hostsidetests/securitybulletin/res/cve_2020_0470.mp4 Binary files differnew file mode 100644 index 00000000000..d77b2f353c4 --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2020_0470.mp4 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 00000000000..1a0726579e1 --- /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 00000000000..30e22d4901c --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0213/poc.cpp @@ -0,0 +1,482 @@ +/* + * 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(), + !bool(android::Codec2Client::CreateFromService("default", true))); + } + } + } + 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/securityPatch/CVE-2020-0470/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0470/Android.bp new file mode 100644 index 00000000000..1876c60bcf0 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0470/Android.bp @@ -0,0 +1,35 @@ +/* + * 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-0470", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + ], + multilib: { + lib32: { + suffix: "32", + }, + lib64: { + shared_libs: [ + "libmediandk", + ], + suffix: "64", + }, + }, +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2020-0470/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0470/poc.cpp new file mode 100644 index 00000000000..d434e117f17 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2020-0470/poc.cpp @@ -0,0 +1,131 @@ +/* + * 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 <stdlib.h> +#include "../includes/common.h" + +// This PoC is only for 64-bit builds +#if _64_BIT +#include <media/NdkMediaCodec.h> + +#define DEQUEUE_BUFFER_TIMEOUT_MICROSECONDS 1000 +#define TOTAL_TIMEOUT_MICROSECONDS (300 * 1000 * 1000) +#define FILE_SIZE 72 +#define VIDEO_MAX_WIDTH 176 +#define VIDEO_MAX_HEIGHT 144 +#endif /* _64_BIT */ + +int main(int argc, char *argv[]) { + (void)argc; + (void)argv; + +// This PoC is only for 64-bit builds +#if _64_BIT + if (argc != 2) { + return EXIT_FAILURE; + } + + FILE *inFile = fopen(argv[1], "rb"); + if (!inFile) { + return EXIT_FAILURE; + } + AMediaCodec *codec; + media_status_t status; + int64_t inActiveTime = 0ll; + bool isEncoder = false; + + codec = AMediaCodec_createCodecByName("c2.android.av1-aom.decoder"); + if (!codec) { + fclose(inFile); + return EXIT_FAILURE; + } + /* Set Format */ + AMediaFormat *format = AMediaFormat_new(); + if (!format) { + fclose(inFile); + AMediaCodec_delete(codec); + return EXIT_FAILURE; + } + AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "video/av01"); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, FILE_SIZE); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, VIDEO_MAX_WIDTH); + AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, VIDEO_MAX_HEIGHT); + AMediaCodec_configure(codec, format, nullptr, nullptr, isEncoder); + AMediaCodec_start(codec); + + size_t filePos = 0; + bool inputEOS = false; + while (inActiveTime < TOTAL_TIMEOUT_MICROSECONDS) { + /* Queue input data */ + if (!inputEOS) { + uint32_t bufferFlags = 0; + ssize_t inIdx = + AMediaCodec_dequeueInputBuffer(codec, DEQUEUE_BUFFER_TIMEOUT_MICROSECONDS); + if (inIdx >= 0) { + ssize_t bytesRead = 0; + size_t bufSize; + uint8_t *buf = AMediaCodec_getInputBuffer(codec, inIdx, &bufSize); + if (filePos < FILE_SIZE) { + bytesRead = fread(buf, 1, FILE_SIZE, inFile); + filePos += FILE_SIZE; + fseek(inFile, filePos, SEEK_SET); + } + if (bytesRead <= 0) { + bytesRead = 0; + inputEOS = true; + bufferFlags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM; + } + status = AMediaCodec_queueInputBuffer(codec, inIdx, 0, bytesRead, 0, bufferFlags); + if (status != AMEDIA_OK) { + break; + } + inActiveTime = 0; + } else if (inIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { + inActiveTime += DEQUEUE_BUFFER_TIMEOUT_MICROSECONDS; + } else { + break; + } + } + /* Dequeue output */ + AMediaCodecBufferInfo info; + ssize_t outIdx = + AMediaCodec_dequeueOutputBuffer(codec, &info, DEQUEUE_BUFFER_TIMEOUT_MICROSECONDS); + if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED || + outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { + inActiveTime = 0; + } else if (outIdx >= 0) { + status = AMediaCodec_releaseOutputBuffer(codec, outIdx, false); + if (status != AMEDIA_OK) { + break; + } + if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) { + break; + } + inActiveTime = 0; + } else if (outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { + inActiveTime += DEQUEUE_BUFFER_TIMEOUT_MICROSECONDS; + } else { + break; + } + } + AMediaFormat_delete(format); + AMediaCodec_stop(codec); + AMediaCodec_delete(codec); + fclose(inFile); +#endif /* _64_BIT */ + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java index 863297b679f..d62b0869331 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java @@ -505,6 +505,33 @@ public class TestMedia extends SecurityTestCase { ******************************************************************************/ /** + * 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 + */ + @SecurityTest(minPatchLevel = "2020-12") + @Test + public void testPocCVE_2020_0470() throws Exception { + String inputFiles[] = {"cve_2020_0470.mp4"}; + String processPatternStrings[] = {"media\\.swcodec"}; + AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2020-0470", + AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice(), + processPatternStrings); + } + + /** * b/120426980 * Vulnerability Behaviour: SIGABRT in self */ |