1 /******************************************************************************
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
24 using namespace android;
25
26 class LinearBuffer : public C2Buffer {
27 public:
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block)28 explicit LinearBuffer(const std::shared_ptr<C2LinearBlock>& block)
29 : C2Buffer({block->share(block->offset(), block->size(), ::C2Fence())}) {}
30
LinearBuffer(const std::shared_ptr<C2LinearBlock> & block,size_t size)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 */
38 struct CodecListener : public C2Component::Listener {
39 public:
CodecListenerCodecListener40 CodecListener(const std::function<void(std::weak_ptr<C2Component> comp,
41 std::list<std::unique_ptr<C2Work>>& workItems)>
42 fn = nullptr)
43 : callBack(fn) {}
onWorkDone_nbCodecListener44 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
onTripped_nbCodecListener51 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
onError_nbCodecListener57 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 */
searchForMarker()69 bool 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
parse()82 void 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
getFrame()116 FrameData Codec2Fuzzer::BufferSource::getFrame() {
117 FrameData frame = mFrameList.back();
118 mFrameList.pop_back();
119 return frame;
120 }
121
handleWorkDone(std::weak_ptr<C2Component> comp,std::list<std::unique_ptr<C2Work>> & workItems)122 void 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
initDecoder()147 bool Codec2Fuzzer::initDecoder() {
148 std::vector<std::tuple<C2String, C2ComponentFactory::CreateCodec2FactoryFunc,
149 C2ComponentFactory::DestroyCodec2FactoryFunc>> codec2FactoryFunc;
150
151 codec2FactoryFunc.emplace_back(
152 std::make_tuple(C2COMPONENTNAME, &CreateCodec2Factory, &DestroyCodec2Factory));
153
154 std::shared_ptr<C2ComponentStore> componentStore = GetTestComponentStore(codec2FactoryFunc);
155 if (!componentStore) {
156 return false;
157 }
158
159 std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
160 if (!allocatorStore) {
161 return false;
162 }
163
164 c2_status_t status =
165 allocatorStore->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator);
166 if (status != C2_OK) {
167 return false;
168 }
169
170 mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
171 if (!mLinearPool) {
172 return false;
173 }
174
175 for (int32_t i = 0; i < kNumberOfC2WorkItems; ++i) {
176 mWorkQueue.emplace_back(new C2Work);
177 }
178
179 status = componentStore->createComponent(C2COMPONENTNAME, &mComponent);
180 if (status != C2_OK) {
181 return false;
182 }
183
184 status = componentStore->createInterface(C2COMPONENTNAME, &mInterface);
185 if (status != C2_OK) {
186 return false;
187 }
188
189 C2ComponentKindSetting kind;
190 C2ComponentDomainSetting domain;
191 status = mInterface->query_vb({&kind, &domain}, {}, C2_MAY_BLOCK, nullptr);
192 if (status != C2_OK) {
193 return false;
194 }
195
196 std::vector<C2Param*> configParams;
197 C2StreamPictureSizeInfo::input inputSize(0u, kWidthOfVideo, kHeightOfVideo);
198 C2StreamSampleRateInfo::output sampleRateInfo(0u, kSamplingRateOfAudio);
199 C2StreamChannelCountInfo::output channelCountInfo(0u, kChannelsOfAudio);
200 if (domain.value == DOMAIN_VIDEO) {
201 configParams.push_back(&inputSize);
202 } else if (domain.value == DOMAIN_AUDIO) {
203 configParams.push_back(&sampleRateInfo);
204 configParams.push_back(&channelCountInfo);
205 }
206
207 mListener.reset(new CodecListener(
208 [this](std::weak_ptr<C2Component> comp, std::list<std::unique_ptr<C2Work>>& workItems) {
209 handleWorkDone(comp, workItems);
210 }));
211 if (!mListener) {
212 return false;
213 }
214
215 status = mComponent->setListener_vb(mListener, C2_DONT_BLOCK);
216 if (status != C2_OK) {
217 return false;
218 }
219
220 std::vector<std::unique_ptr<C2SettingResult>> failures;
221 componentStore->config_sm(configParams, &failures);
222 if (failures.size() != 0) {
223 return false;
224 }
225
226 status = mComponent->start();
227 if (status != C2_OK) {
228 return false;
229 }
230
231 return true;
232 }
233
deInitDecoder()234 void Codec2Fuzzer::deInitDecoder() {
235 mComponent->stop();
236 mComponent->reset();
237 mComponent->release();
238 mComponent = nullptr;
239 }
240
decodeFrames(const uint8_t * data,size_t size)241 void Codec2Fuzzer::decodeFrames(const uint8_t* data, size_t size) {
242 std::unique_ptr<BufferSource> bufferSource = std::make_unique<BufferSource>(data, size);
243 if (!bufferSource) {
244 return;
245 }
246 bufferSource->parse();
247 c2_status_t status = C2_OK;
248 size_t numFrames = 0;
249 while (!bufferSource->isEos()) {
250 uint8_t* frame = nullptr;
251 size_t frameSize = 0;
252 FrameData frameData = bufferSource->getFrame();
253 frame = std::get<0>(frameData);
254 frameSize = std::get<1>(frameData);
255
256 std::unique_ptr<C2Work> work;
257 {
258 std::unique_lock<std::mutex> lock(mQueueLock);
259 if (mWorkQueue.empty()) mQueueCondition.wait_for(lock, kC2FuzzerTimeOut);
260 if (!mWorkQueue.empty()) {
261 work.swap(mWorkQueue.front());
262 mWorkQueue.pop_front();
263 } else {
264 return;
265 }
266 }
267
268 work->input.flags = (C2FrameData::flags_t)std::get<2>(frameData);
269 work->input.ordinal.timestamp = 0;
270 work->input.ordinal.frameIndex = ++numFrames;
271 work->input.buffers.clear();
272 int32_t alignedSize = C2FUZZER_ALIGN(frameSize, PAGE_SIZE);
273
274 std::shared_ptr<C2LinearBlock> block;
275 status = mLinearPool->fetchLinearBlock(
276 alignedSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
277 if (status != C2_OK || block == nullptr) {
278 return;
279 }
280
281 C2WriteView view = block->map().get();
282 if (view.error() != C2_OK) {
283 return;
284 }
285 memcpy(view.base(), frame, frameSize);
286 work->input.buffers.emplace_back(new LinearBuffer(block, frameSize));
287 work->worklets.clear();
288 work->worklets.emplace_back(new C2Worklet);
289
290 std::list<std::unique_ptr<C2Work>> items;
291 items.push_back(std::move(work));
292 status = mComponent->queue_nb(&items);
293 if (status != C2_OK) {
294 return;
295 }
296 }
297 std::unique_lock<std::mutex> waitForDecodeComplete(mDecodeCompleteMutex);
298 mConditionalVariable.wait_for(waitForDecodeComplete, kC2FuzzerTimeOut, [this] { return mEos; });
299 std::list<std::unique_ptr<C2Work>> c2flushedWorks;
300 mComponent->flush_sm(C2Component::FLUSH_COMPONENT, &c2flushedWorks);
301 }
302
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)303 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
304 if (size < 1) {
305 return 0;
306 }
307 Codec2Fuzzer* codec = new Codec2Fuzzer();
308 if (!codec) {
309 return 0;
310 }
311 if (codec->initDecoder()) {
312 codec->decodeFrames(data, size);
313 }
314 delete codec;
315 return 0;
316 }
317