blob: 719962dea24f642716e61b7b087f9852659a2bf9 [file] [log] [blame]
/******************************************************************************
*
* 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.
*
*****************************************************************************
* Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
*/
#include <stdio.h>
#include <C2Fuzzer.h>
using namespace android;
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_nb(), onTripped_nb(), onError_nb() for C2 Components
*/
struct CodecListener : public C2Component::Listener {
public:
CodecListener(const std::function<void(std::weak_ptr<C2Component> comp,
std::list<std::unique_ptr<C2Work>>& workItems)>
fn = nullptr)
: callBack(fn) {}
virtual void onWorkDone_nb(const std::weak_ptr<C2Component> comp,
std::list<std::unique_ptr<C2Work>> workItems) {
if (callBack) {
callBack(comp, workItems);
}
}
virtual void onTripped_nb(const std::weak_ptr<C2Component> comp,
const std::vector<std::shared_ptr<C2SettingResult>> settingResults) {
(void)comp;
(void)settingResults;
}
virtual void onError_nb(const std::weak_ptr<C2Component> comp, uint32_t errorCode) {
(void)comp;
(void)errorCode;
}
std::function<void(std::weak_ptr<C2Component> comp,
std::list<std::unique_ptr<C2Work>>& workItems)> callBack;
};
/**
* Buffer source implementations to identify a frame and its size
*/
bool Codec2Fuzzer::BufferSource::searchForMarker() {
while (true) {
if (isMarker()) {
return true;
}
--mReadIndex;
if (mReadIndex > mSize) {
break;
}
}
return false;
}
void Codec2Fuzzer::BufferSource::parse() {
bool isFrameAvailable = true;
size_t bytesRemaining = mSize;
while (isFrameAvailable) {
isFrameAvailable = searchForMarker();
if (isFrameAvailable) {
size_t location = mReadIndex + kMarkerSize;
bool isCSD = isCSDMarker(location);
location += kMarkerSuffixSize;
uint8_t* framePtr = const_cast<uint8_t*>(&mData[location]);
size_t frameSize = bytesRemaining - location;
uint32_t flags = 0;
if (mFrameList.empty()) {
flags |= C2FrameData::FLAG_END_OF_STREAM;
} else if (isCSD) {
flags |= C2FrameData::FLAG_CODEC_CONFIG;
}
mFrameList.emplace_back(std::make_tuple(framePtr, frameSize, flags));
bytesRemaining -= (frameSize + kMarkerSize + kMarkerSuffixSize);
--mReadIndex;
}
}
if (mFrameList.empty()) {
/**
* Scenario where input data does not contain the custom frame markers.
* Hence feed the entire data as single frame.
*/
mFrameList.emplace_back(
std::make_tuple(const_cast<uint8_t*>(mData), 0, C2FrameData::FLAG_END_OF_STREAM));
mFrameList.emplace_back(
std::make_tuple(const_cast<uint8_t*>(mData), mSize, C2FrameData::FLAG_CODEC_CONFIG));
}
}
FrameData Codec2Fuzzer::BufferSource::getFrame() {
FrameData frame = mFrameList.back();
mFrameList.pop_back();
return frame;
}
void Codec2Fuzzer::handleWorkDone(std::weak_ptr<C2Component> comp,
std::list<std::unique_ptr<C2Work>>& workItems) {
(void)comp;
for (std::unique_ptr<C2Work>& work : workItems) {
if (!work->worklets.empty()) {
if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
mEos = (work->worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
work->input.buffers.clear();
work->worklets.clear();
{
std::unique_lock<std::mutex> lock(mQueueLock);
mWorkQueue.push_back(std::move(work));
mQueueCondition.notify_all();
}
if (mEos) {
{
std::lock_guard<std::mutex> waitForDecodeComplete(mDecodeCompleteMutex);
}
mConditionalVariable.notify_one();
}
}
}
}
}
bool Codec2Fuzzer::initDecoder() {
std::vector<std::tuple<C2String, C2ComponentFactory::CreateCodec2FactoryFunc,
C2ComponentFactory::DestroyCodec2FactoryFunc>> codec2FactoryFunc;
codec2FactoryFunc.emplace_back(
std::make_tuple(C2COMPONENTNAME, &CreateCodec2Factory, &DestroyCodec2Factory));
std::shared_ptr<C2ComponentStore> componentStore = GetTestComponentStore(codec2FactoryFunc);
if (!componentStore) {
return false;
}
std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
if (!allocatorStore) {
return false;
}
c2_status_t status =
allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator);
if (status != C2_OK) {
return false;
}
mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
if (!mLinearPool) {
return false;
}
for (int32_t i = 0; i < kNumberOfC2WorkItems; ++i) {
mWorkQueue.emplace_back(new C2Work);
}
status = componentStore->createComponent(C2COMPONENTNAME, &mComponent);
if (status != C2_OK) {
return false;
}
status = componentStore->createInterface(C2COMPONENTNAME, &mInterface);
if (status != C2_OK) {
return false;
}
C2ComponentKindSetting kind;
C2ComponentDomainSetting domain;
status = mInterface->query_vb({&kind, &domain}, {}, C2_MAY_BLOCK, nullptr);
if (status != C2_OK) {
return false;
}
std::vector<C2Param*> configParams;
C2StreamPictureSizeInfo::input inputSize(0u, kWidthOfVideo, kHeightOfVideo);
C2StreamSampleRateInfo::output sampleRateInfo(0u, kSamplingRateOfAudio);
C2StreamChannelCountInfo::output channelCountInfo(0u, kChannelsOfAudio);
if (domain.value == DOMAIN_VIDEO) {
configParams.push_back(&inputSize);
} else if (domain.value == DOMAIN_AUDIO) {
configParams.push_back(&sampleRateInfo);
configParams.push_back(&channelCountInfo);
}
mListener.reset(new CodecListener(
[this](std::weak_ptr<C2Component> comp, std::list<std::unique_ptr<C2Work>>& workItems) {
handleWorkDone(comp, workItems);
}));
if (!mListener) {
return false;
}
status = mComponent->setListener_vb(mListener, C2_DONT_BLOCK);
if (status != C2_OK) {
return false;
}
std::vector<std::unique_ptr<C2SettingResult>> failures;
componentStore->config_sm(configParams, &failures);
if (failures.size() != 0) {
return false;
}
status = mComponent->start();
if (status != C2_OK) {
return false;
}
return true;
}
void Codec2Fuzzer::deInitDecoder() {
mComponent->stop();
mComponent->reset();
mComponent->release();
mComponent = nullptr;
}
void Codec2Fuzzer::decodeFrames(const uint8_t* data, size_t size) {
static const size_t kPageSize = getpagesize();
std::unique_ptr<BufferSource> bufferSource = std::make_unique<BufferSource>(data, size);
if (!bufferSource) {
return;
}
bufferSource->parse();
c2_status_t status = C2_OK;
size_t numFrames = 0;
int32_t iterationCount = 0;
while (!bufferSource->isEos() && ++iterationCount <= kMaxIterations) {
uint8_t* frame = nullptr;
size_t frameSize = 0;
FrameData frameData = bufferSource->getFrame();
frame = std::get<0>(frameData);
frameSize = std::get<1>(frameData);
std::unique_ptr<C2Work> work;
{
std::unique_lock<std::mutex> lock(mQueueLock);
if (mWorkQueue.empty()) mQueueCondition.wait_for(lock, kC2FuzzerTimeOut);
if (!mWorkQueue.empty()) {
work.swap(mWorkQueue.front());
mWorkQueue.pop_front();
} else {
return;
}
}
work->input.flags = (C2FrameData::flags_t)std::get<2>(frameData);
work->input.ordinal.timestamp = 0;
work->input.ordinal.frameIndex = ++numFrames;
work->input.buffers.clear();
int32_t alignedSize = C2FUZZER_ALIGN(frameSize, kPageSize);
std::shared_ptr<C2LinearBlock> block;
status = mLinearPool->fetchLinearBlock(
alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
if (status != C2_OK || block == nullptr) {
return;
}
C2WriteView view = block->map().get();
if (view.error() != C2_OK) {
return;
}
memcpy(view.base(), frame, frameSize);
work->input.buffers.emplace_back(new LinearBuffer(block, frameSize));
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
status = mComponent->queue_nb(&items);
if (status != C2_OK) {
return;
}
}
std::unique_lock<std::mutex> waitForDecodeComplete(mDecodeCompleteMutex);
mConditionalVariable.wait_for(waitForDecodeComplete, kC2FuzzerTimeOut, [this] { return mEos; });
std::list<std::unique_ptr<C2Work>> c2flushedWorks;
mComponent->flush_sm(C2Component::FLUSH_COMPONENT, &c2flushedWorks);
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size < 1) {
return 0;
}
Codec2Fuzzer* codec = new Codec2Fuzzer();
if (!codec) {
return 0;
}
if (codec->initDecoder()) {
codec->decodeFrames(data, size);
}
delete codec;
return 0;
}