• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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