blob: 71956a24b6c53d981101005ca911c6eb059d8ba1 [file] [log] [blame]
S Vasudev Prasadd064e172020-05-11 13:10:16 +05301/******************************************************************************
2 *
3 * Copyright (C) 2020 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at:
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *****************************************************************************
18 * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
19 */
20#include <stdio.h>
21
22#include <C2Fuzzer.h>
23
24using namespace android;
25
26class LinearBuffer : public C2Buffer {
27 public:
28 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
29 : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
30
31 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block, size_t size)
32 : C2Buffer({block->share(block->offset(), size, ::C2Fence())}) {}
33};
34
35/**
36 * Handle Callback functions onWorkDone_nb(), onTripped_nb(), onError_nb() for C2 Components
37 */
38struct CodecListener : public C2Component::Listener {
39 public:
40 CodecListener(const std::function<void(std::weak_ptr<C2Component> comp,
41 std::list<std::unique_ptr<C2Work>>& workItems)>
42 fn = nullptr)
43 : callBack(fn) {}
44 virtual void onWorkDone_nb(const std::weak_ptr<C2Component> comp,
45 std::list<std::unique_ptr<C2Work>> workItems) {
46 if (callBack) {
47 callBack(comp, workItems);
48 }
49 }
50
51 virtual void onTripped_nb(const std::weak_ptr<C2Component> comp,
52 const std::vector<std::shared_ptr<C2SettingResult>> settingResults) {
53 (void)comp;
54 (void)settingResults;
55 }
56
57 virtual void onError_nb(const std::weak_ptr<C2Component> comp, uint32_t errorCode) {
58 (void)comp;
59 (void)errorCode;
60 }
61
62 std::function<void(std::weak_ptr<C2Component> comp,
63 std::list<std::unique_ptr<C2Work>>& workItems)> callBack;
64};
65
66/**
67 * Buffer source implementations to identify a frame and its size
68 */
69bool Codec2Fuzzer::BufferSource::searchForMarker() {
70 while (true) {
71 if (isMarker()) {
72 return true;
73 }
74 --mReadIndex;
75 if (mReadIndex > mSize) {
76 break;
77 }
78 }
79 return false;
80}
81
82void Codec2Fuzzer::BufferSource::parse() {
83 bool isFrameAvailable = true;
84 size_t bytesRemaining = mSize;
85 while (isFrameAvailable) {
86 isFrameAvailable = searchForMarker();
87 if (isFrameAvailable) {
88 size_t location = mReadIndex + kMarkerSize;
89 bool isCSD = isCSDMarker(location);
90 location += kMarkerSuffixSize;
91 uint8_t* framePtr = const_cast<uint8_t*>(&mData[location]);
92 size_t frameSize = bytesRemaining - location;
93 uint32_t flags = 0;
94 if (mFrameList.empty()) {
95 flags |= C2FrameData::FLAG_END_OF_STREAM;
96 } else if (isCSD) {
97 flags |= C2FrameData::FLAG_CODEC_CONFIG;
98 }
99 mFrameList.emplace_back(std::make_tuple(framePtr, frameSize, flags));
100 bytesRemaining -= (frameSize + kMarkerSize + kMarkerSuffixSize);
101 --mReadIndex;
102 }
103 }
104 if (mFrameList.empty()) {
105 /**
106 * Scenario where input data does not contain the custom frame markers.
107 * Hence feed the entire data as single frame.
108 */
109 mFrameList.emplace_back(
110 std::make_tuple(const_cast<uint8_t*>(mData), 0, C2FrameData::FLAG_END_OF_STREAM));
111 mFrameList.emplace_back(
112 std::make_tuple(const_cast<uint8_t*>(mData), mSize, C2FrameData::FLAG_CODEC_CONFIG));
113 }
114}
115
116FrameData Codec2Fuzzer::BufferSource::getFrame() {
117 FrameData frame = mFrameList.back();
118 mFrameList.pop_back();
119 return frame;
120}
121
122void Codec2Fuzzer::handleWorkDone(std::weak_ptr<C2Component> comp,
123 std::list<std::unique_ptr<C2Work>>& workItems) {
124 (void)comp;
125 for (std::unique_ptr<C2Work>& work : workItems) {
126 if (!work->worklets.empty()) {
127 if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
128 mEos = (work->worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
129 work->input.buffers.clear();
130 work->worklets.clear();
131 {
132 std::unique_lock<std::mutex> lock(mQueueLock);
133 mWorkQueue.push_back(std::move(work));
134 mQueueCondition.notify_all();
135 }
136 if (mEos) {
137 {
138 std::lock_guard<std::mutex> waitForDecodeComplete(mDecodeCompleteMutex);
139 }
140 mConditionalVariable.notify_one();
141 }
142 }
143 }
144 }
145}
146
147bool Codec2Fuzzer::initDecoder() {
148 std::vector<std::tuple<C2String, C2ComponentFactory::CreateCodec2FactoryFunc,
149 C2ComponentFactory::DestroyCodec2FactoryFunc>> codec2FactoryFunc;
150
151 codec2FactoryFunc.emplace_back(std::make_tuple(C2COMPONENTNAME,
152 &CreateCodec2Factory,
153 &DestroyCodec2Factory));
154
155 std::shared_ptr<C2ComponentStore> componentStore = GetTestComponentStore(codec2FactoryFunc);
156 if (!componentStore) {
157 return false;
158 }
159
160 std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
161 if (!allocatorStore) {
162 return false;
163 }
164
165 c2_status_t status =
166 allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator);
167 if (status != C2_OK) {
168 return false;
169 }
170
171 mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
172 if (!mLinearPool) {
173 return false;
174 }
175
176 for (int32_t i = 0; i < kNumberOfC2WorkItems; ++i) {
177 mWorkQueue.emplace_back(new C2Work);
178 }
179
180 status = componentStore->createComponent(C2COMPONENTNAME, &mComponent);
181 if (status != C2_OK) {
182 return false;
183 }
184
185 status = componentStore->createInterface(C2COMPONENTNAME, &mInterface);
186 if (status != C2_OK) {
187 return false;
188 }
189
190 C2ComponentKindSetting kind;
191 C2ComponentDomainSetting domain;
192 status = mInterface->query_vb({&kind, &domain}, {}, C2_MAY_BLOCK, nullptr);
193 if (status != C2_OK) {
194 return false;
195 }
196
197 std::vector<C2Param*> configParams;
198 if (domain.value == DOMAIN_VIDEO) {
199 C2StreamPictureSizeInfo::input inputSize(0u, kWidthOfVideo, kHeightOfVideo);
200 configParams.push_back(&inputSize);
201 } else if (domain.value == DOMAIN_AUDIO) {
202 C2StreamSampleRateInfo::output sampleRateInfo(0u, kSamplingRateOfAudio);
203 C2StreamChannelCountInfo::output channelCountInfo(0u, kChannelsOfAudio);
204 configParams.push_back(&sampleRateInfo);
205 configParams.push_back(&channelCountInfo);
206 }
207
208 mListener.reset(new CodecListener(
209 [this](std::weak_ptr<C2Component> comp, std::list<std::unique_ptr<C2Work>>& workItems) {
210 handleWorkDone(comp, workItems);
211 }));
212 if (!mListener) {
213 return false;
214 }
215
216 status = mComponent->setListener_vb(mListener, C2_DONT_BLOCK);
217 if (status != C2_OK) {
218 return false;
219 }
220
221 std::vector<std::unique_ptr<C2SettingResult>> failures;
222 componentStore->config_sm(configParams, &failures);
223 if (failures.size() != 0) {
224 return false;
225 }
226
227 status = mComponent->start();
228 if (status != C2_OK) {
229 return false;
230 }
231
232 return true;
233}
234
235void Codec2Fuzzer::deInitDecoder() {
236 mComponent->stop();
237 mComponent->reset();
238 mComponent->release();
239 mComponent = nullptr;
240}
241
242void Codec2Fuzzer::decodeFrames(const uint8_t* data, size_t size) {
243 mBufferSource = new BufferSource(data, size);
244 if (!mBufferSource) {
245 return;
246 }
247 mBufferSource->parse();
248 c2_status_t status = C2_OK;
249 size_t numFrames = 0;
250 while (!mBufferSource->isEos()) {
251 uint8_t* frame = nullptr;
252 size_t frameSize = 0;
253 FrameData frameData = mBufferSource->getFrame();
254 frame = std::get<0>(frameData);
255 frameSize = std::get<1>(frameData);
256
257 std::unique_ptr<C2Work> work;
258 {
259 std::unique_lock<std::mutex> lock(mQueueLock);
260 if (mWorkQueue.empty()) mQueueCondition.wait_for(lock, kC2FuzzerTimeOut);
261 if (!mWorkQueue.empty()) {
262 work.swap(mWorkQueue.front());
263 mWorkQueue.pop_front();
264 } else {
265 return;
266 }
267 }
268
269 work->input.flags = (C2FrameData::flags_t)std::get<2>(frameData);
270 work->input.ordinal.timestamp = 0;
271 work->input.ordinal.frameIndex = ++numFrames;
272 work->input.buffers.clear();
273 int32_t alignedSize = C2FUZZER_ALIGN(frameSize, PAGE_SIZE);
274
275 std::shared_ptr<C2LinearBlock> block;
276 status = mLinearPool->fetchLinearBlock(
277 alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
278 if (status != C2_OK || block == nullptr) {
279 return;
280 }
281
282 C2WriteView view = block->map().get();
283 if (view.error() != C2_OK) {
284 return;
285 }
286 memcpy(view.base(), frame, frameSize);
287 work->input.buffers.emplace_back(new LinearBuffer(block, frameSize));
288 work->worklets.clear();
289 work->worklets.emplace_back(new C2Worklet);
290
291 std::list<std::unique_ptr<C2Work>> items;
292 items.push_back(std::move(work));
293 status = mComponent->queue_nb(&items);
294 if (status != C2_OK) {
295 return;
296 }
297 }
298 std::unique_lock<std::mutex> waitForDecodeComplete(mDecodeCompleteMutex);
299 mConditionalVariable.wait_for(waitForDecodeComplete, kC2FuzzerTimeOut, [this] { return mEos; });
300 std::list<std::unique_ptr<C2Work>> c2flushedWorks;
301 mComponent->flush_sm(C2Component::FLUSH_COMPONENT, &c2flushedWorks);
302 delete mBufferSource;
303}
304
305extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
306 if (size < 1) {
307 return 0;
308 }
309 Codec2Fuzzer* codec = new Codec2Fuzzer();
310 if (!codec) {
311 return 0;
312 }
313 if (codec->initDecoder()) {
314 codec->decodeFrames(data, size);
315 }
316 delete codec;
317 return 0;
318}