1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <NdkMediaCodecFuzzerBase.h>
17 #include <media/NdkMediaFormatPriv.h>
18 #include <functional>
19 #include <mutex>
20 #include <queue>
21 #include <thread>
22 
23 using namespace android;
24 using namespace std;
25 
26 constexpr int32_t kMaxCryptoInfoAPIs = 3;
27 constexpr int32_t kMaxNdkCodecAPIs = 5;
28 
29 template <typename T>
30 class CallBackQueue {
31   public:
push(T elem)32     void push(T elem) {
33         bool needsNotify = false;
34         {
35             unique_lock<mutex> lock(mMutex);
36             needsNotify = mQueue.empty();
37             mQueue.push(std::move(elem));
38         }
39         if (needsNotify) {
40             mQueueNotEmptyCondition.notify_one();
41         }
42     }
43 
pop()44     T pop() {
45         unique_lock<mutex> lock(mMutex);
46         if (mQueue.empty()) {
47             mQueueNotEmptyCondition.wait(lock, [this]() { return !mQueue.empty(); });
48         }
49         auto result = mQueue.front();
50         mQueue.pop();
51         return result;
52     }
53 
54   private:
55     mutex mMutex;
56     std::queue<T> mQueue;
57     std::condition_variable mQueueNotEmptyCondition;
58 };
59 
60 class CallBackHandle {
61   public:
CallBackHandle()62     CallBackHandle() : mSawError(false), mIsDone(false) {}
63 
~CallBackHandle()64     virtual ~CallBackHandle() {}
65 
66     void ioThread();
67 
68     // Implementation in child class (Decoder/Encoder)
invokeInputBufferAPI(AMediaCodec * codec,int32_t index)69     virtual void invokeInputBufferAPI(AMediaCodec* codec, int32_t index) {
70         (void)codec;
71         (void)index;
72     }
onFormatChanged(AMediaCodec * codec,AMediaFormat * format)73     virtual void onFormatChanged(AMediaCodec* codec, AMediaFormat* format) {
74         (void)codec;
75         (void)format;
76     }
receiveError(void)77     virtual void receiveError(void) {}
invokeOutputBufferAPI(AMediaCodec * codec,int32_t index,AMediaCodecBufferInfo * bufferInfo)78     virtual void invokeOutputBufferAPI(AMediaCodec* codec, int32_t index,
79                                        AMediaCodecBufferInfo* bufferInfo) {
80         (void)codec;
81         (void)index;
82         (void)bufferInfo;
83     }
84 
85     // Keep a queue of all function callbacks.
86     typedef function<void()> IOTask;
87     CallBackQueue<IOTask> mIOQueue;
88     bool mSawError;
89     bool mIsDone;
90 };
91 
ioThread()92 void CallBackHandle::ioThread() {
93     while (!mIsDone && !mSawError) {
94         auto task = mIOQueue.pop();
95         task();
96     }
97 }
98 
onAsyncInputAvailable(AMediaCodec * codec,void * userdata,int32_t index)99 static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
100     CallBackHandle* self = (CallBackHandle*)userdata;
101     self->mIOQueue.push([self, codec, index]() { self->invokeInputBufferAPI(codec, index); });
102 }
103 
onAsyncOutputAvailable(AMediaCodec * codec,void * userdata,int32_t index,AMediaCodecBufferInfo * bufferInfo)104 static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
105                                    AMediaCodecBufferInfo* bufferInfo) {
106     CallBackHandle* self = (CallBackHandle*)userdata;
107     AMediaCodecBufferInfo bufferInfoCopy = *bufferInfo;
108     self->mIOQueue.push([self, codec, index, bufferInfoCopy]() {
109         AMediaCodecBufferInfo bc = bufferInfoCopy;
110         self->invokeOutputBufferAPI(codec, index, &bc);
111     });
112 }
113 
onAsyncFormatChanged(AMediaCodec * codec,void * userdata,AMediaFormat * format)114 static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
115     (void)codec;
116     (void)userdata;
117     (void)format;
118 };
119 
onAsyncError(AMediaCodec * codec,void * userdata,media_status_t err,int32_t actionCode,const char * detail)120 static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t err, int32_t actionCode,
121                          const char* detail) {
122     CallBackHandle* self = (CallBackHandle*)userdata;
123     self->mSawError = true;
124     self->receiveError();
125     (void)codec;
126     (void)err;
127     (void)actionCode;
128     (void)detail;
129 };
130 
131 class NdkAsyncCodecFuzzer : public NdkMediaCodecFuzzerBase, public CallBackHandle {
132   public:
NdkAsyncCodecFuzzer(const uint8_t * data,size_t size)133     NdkAsyncCodecFuzzer(const uint8_t* data, size_t size)
134         : NdkMediaCodecFuzzerBase(), mFdp(data, size) {
135         setFdp(&mFdp);
136         mStopCodec = false;
137         mSawInputEOS = false;
138         mSignalledError = false;
139         mIsEncoder = false;
140         mNumOfFrames = 0;
141         mNumInputFrames = 0;
142     };
~NdkAsyncCodecFuzzer()143     ~NdkAsyncCodecFuzzer() {
144         mIOThreadPool->stop();
145         delete (mIOThreadPool);
146     };
147 
148     void process();
149 
codecOnFrameRendered(AMediaCodec * codec,void * userdata,int64_t mediaTimeUs,int64_t systemNano)150     static void codecOnFrameRendered(AMediaCodec* codec, void* userdata, int64_t mediaTimeUs,
151                                      int64_t systemNano) {
152         (void)codec;
153         (void)userdata;
154         (void)mediaTimeUs;
155         (void)systemNano;
156     };
157     class ThreadPool {
158       public:
159         void start();
160         void queueJob(const std::function<void()>& job);
161         void stop();
162 
163       private:
164         void ThreadLoop();
165         bool mShouldTerminate = false;
166         std::vector<std::thread> mThreads;
167         std::mutex mQueueMutex;
168         std::condition_variable mQueueMutexCondition;
169         std::queue<std::function<void()>> mJobs;
170     };
171 
172   private:
173     FuzzedDataProvider mFdp;
174     AMediaCodec* mCodec = nullptr;
175     void invokeCodecCryptoInfoAPI();
176     void invokekAsyncCodecAPIs(bool isEncoder);
177     void invokeAsyncCodeConfigAPI();
178     void invokeInputBufferAPI(AMediaCodec* codec, int32_t bufferIndex);
179     void invokeOutputBufferAPI(AMediaCodec* codec, int32_t bufferIndex,
180                                AMediaCodecBufferInfo* bufferInfo);
181     void invokeFormatAPI(AMediaCodec* codec);
182     void receiveError();
183     bool mStopCodec;
184     bool mSawInputEOS;
185     bool mSignalledError;
186     int32_t mNumOfFrames;
187     int32_t mNumInputFrames;
188     mutable Mutex mMutex;
189     bool mIsEncoder;
190     ThreadPool* mIOThreadPool = new ThreadPool();
191 };
192 
start()193 void NdkAsyncCodecFuzzer::ThreadPool::start() {
194     const uint32_t numThreads = std::thread::hardware_concurrency();
195     mThreads.resize(numThreads);
196     for (uint32_t i = 0; i < numThreads; ++i) {
197         mThreads.at(i) = std::thread(&ThreadPool::ThreadLoop, this);
198     }
199 }
200 
ThreadLoop()201 void NdkAsyncCodecFuzzer::ThreadPool::ThreadLoop() {
202     while (true) {
203         std::function<void()> job;
204         {
205             std::unique_lock<std::mutex> lock(mQueueMutex);
206             mQueueMutexCondition.wait(lock, [this] { return !mJobs.empty() || mShouldTerminate; });
207             if (mShouldTerminate) {
208                 return;
209             }
210             job = mJobs.front();
211             mJobs.pop();
212         }
213         job();
214     }
215 }
216 
queueJob(const std::function<void ()> & job)217 void NdkAsyncCodecFuzzer::ThreadPool::queueJob(const std::function<void()>& job) {
218     {
219         std::unique_lock<std::mutex> lock(mQueueMutex);
220         mJobs.push(job);
221     }
222     mQueueMutexCondition.notify_one();
223 }
224 
stop()225 void NdkAsyncCodecFuzzer::ThreadPool::stop() {
226     {
227         std::unique_lock<std::mutex> lock(mQueueMutex);
228         mShouldTerminate = true;
229     }
230     mQueueMutexCondition.notify_all();
231     for (std::thread& active_thread : mThreads) {
232         active_thread.join();
233     }
234     mThreads.clear();
235 }
236 
receiveError(void)237 void NdkAsyncCodecFuzzer::receiveError(void) {
238     mSignalledError = true;
239 }
240 
invokeInputBufferAPI(AMediaCodec * codec,int32_t bufferIndex)241 void NdkAsyncCodecFuzzer::invokeInputBufferAPI(AMediaCodec* codec, int32_t bufferIndex) {
242     size_t bufferSize = 0;
243     Mutex::Autolock autoLock(mMutex);
244     if (mSignalledError) {
245         CallBackHandle::mSawError = true;
246         return;
247     }
248     if (mStopCodec || bufferIndex < 0 || mSawInputEOS) {
249         return;
250     }
251 
252     uint8_t* buffer = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufferSize);
253     if (buffer) {
254         std::vector<uint8_t> bytesRead = mFdp.ConsumeBytes<uint8_t>(
255                 std::min(mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes), bufferSize));
256         memcpy(buffer, bytesRead.data(), bytesRead.size());
257         bufferSize = bytesRead.size();
258     } else {
259         mSignalledError = true;
260         return;
261     }
262 
263     uint32_t flag = 0;
264     if (!bufferSize || mNumInputFrames == mNumOfFrames) {
265         flag |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
266         mSawInputEOS = true;
267     }
268     AMediaCodec_queueInputBuffer(codec, bufferIndex, 0 /* offset */, bufferSize, 0 /* time */,
269                                  flag);
270     mNumInputFrames++;
271 }
272 
invokeOutputBufferAPI(AMediaCodec * codec,int32_t bufferIndex,AMediaCodecBufferInfo * bufferInfo)273 void NdkAsyncCodecFuzzer::invokeOutputBufferAPI(AMediaCodec* codec, int32_t bufferIndex,
274                                                 AMediaCodecBufferInfo* bufferInfo) {
275     size_t bufferSize = 0;
276     Mutex::Autolock autoLock(mMutex);
277 
278     if (mSignalledError) {
279         CallBackHandle::mSawError = true;
280         return;
281     }
282 
283     if (mStopCodec || bufferIndex < 0 || mIsDone) {
284         return;
285     }
286 
287     if (!mIsEncoder) {
288         (void)AMediaCodec_getOutputBuffer(codec, bufferIndex, &bufferSize);
289     }
290     AMediaCodec_releaseOutputBuffer(codec, bufferIndex, mFdp.ConsumeBool());
291     mIsDone = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM));
292 }
293 
invokeFormatAPI(AMediaCodec * codec)294 void NdkAsyncCodecFuzzer::invokeFormatAPI(AMediaCodec* codec) {
295     AMediaFormat* codecFormat = nullptr;
296     if (mFdp.ConsumeBool()) {
297         codecFormat = AMediaCodec_getInputFormat(codec);
298     } else {
299         codecFormat = AMediaCodec_getOutputFormat(codec);
300     }
301     if (codecFormat) {
302         AMediaFormat_delete(codecFormat);
303     }
304 }
305 
invokekAsyncCodecAPIs(bool isEncoder)306 void NdkAsyncCodecFuzzer::invokekAsyncCodecAPIs(bool isEncoder) {
307     ANativeWindow* nativeWindow = nullptr;
308 
309     if (mFdp.ConsumeBool()) {
310         AMediaCodec_createInputSurface(mCodec, &nativeWindow);
311     }
312 
313     if (AMEDIA_OK == AMediaCodec_configure(mCodec, getCodecFormat(), nativeWindow,
314                                            nullptr /* crypto */,
315                                            (isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0))) {
316         mNumOfFrames = mFdp.ConsumeIntegralInRange<size_t>(kMinIterations, kMaxIterations);
317         // Configure codecs to run in async mode.
318         AMediaCodecOnAsyncNotifyCallback callBack = {onAsyncInputAvailable, onAsyncOutputAvailable,
319                                                      onAsyncFormatChanged, onAsyncError};
320         AMediaCodec_setAsyncNotifyCallback(mCodec, callBack, this);
321         mIOThreadPool->queueJob([this] { CallBackHandle::ioThread(); });
322 
323         AMediaCodec_start(mCodec);
324         sleep(5);
325         int32_t count = 0;
326         while (++count <= mNumOfFrames) {
327             int32_t ndkcodecAPI =
328                     mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxNdkCodecAPIs);
329             switch (ndkcodecAPI) {
330                 case 0: {  // get input and output Format
331                     invokeFormatAPI(mCodec);
332                     break;
333                 }
334                 case 1: {
335                     AMediaCodec_signalEndOfInputStream(mCodec);
336                     mSawInputEOS = true;
337                     break;
338                 }
339                 case 2: {  // set parameters
340                     // Create a new parameter and set
341                     AMediaFormat* params = AMediaFormat_new();
342                     AMediaFormat_setInt32(
343                             params, "video-bitrate",
344                             mFdp.ConsumeIntegralInRange<size_t>(kMinIntKeyValue, kMaxIntKeyValue));
345                     AMediaCodec_setParameters(mCodec, params);
346                     AMediaFormat_delete(params);
347                     break;
348                 }
349                 case 3: {  // flush codec
350                     AMediaCodec_flush(mCodec);
351                     if (mFdp.ConsumeBool()) {
352                         AMediaCodec_start(mCodec);
353                     }
354                     break;
355                 }
356                 case 4: {
357                     char* name = nullptr;
358                     AMediaCodec_getName(mCodec, &name);
359                     AMediaCodec_releaseName(mCodec, name);
360                     break;
361                 }
362                 case 5:
363                 default: {
364                     std::vector<uint8_t> userData = mFdp.ConsumeBytes<uint8_t>(
365                             mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
366                     AMediaCodecOnFrameRendered callback = codecOnFrameRendered;
367                     AMediaCodec_setOnFrameRenderedCallback(mCodec, callback, userData.data());
368                     break;
369                 }
370             }
371         }
372         {
373             Mutex::Autolock autoLock(mMutex);
374             mStopCodec = 1;
375             AMediaCodec_stop(mCodec);
376         }
377     }
378 
379     if (nativeWindow) {
380         ANativeWindow_release(nativeWindow);
381     }
382 }
383 
invokeAsyncCodeConfigAPI()384 void NdkAsyncCodecFuzzer::invokeAsyncCodeConfigAPI() {
385     mIOThreadPool->start();
386 
387     while (mFdp.remaining_bytes() > 0) {
388         mIsEncoder = mFdp.ConsumeBool();
389         mCodec = createCodec(mIsEncoder, mFdp.ConsumeBool() /* isCodecForClient */);
390         if (mCodec) {
391             invokekAsyncCodecAPIs(mIsEncoder);
392             AMediaCodec_delete(mCodec);
393         }
394     }
395     mIOThreadPool->stop();
396 }
397 
invokeCodecCryptoInfoAPI()398 void NdkAsyncCodecFuzzer::invokeCodecCryptoInfoAPI() {
399     while (mFdp.remaining_bytes() > 0) {
400         AMediaCodecCryptoInfo* cryptoInfo = getAMediaCodecCryptoInfo();
401         int32_t ndkCryptoInfoAPI =
402                 mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxCryptoInfoAPIs);
403         switch (ndkCryptoInfoAPI) {
404             case 0: {
405                 size_t sizes[kMaxCryptoKey];
406                 AMediaCodecCryptoInfo_getEncryptedBytes(cryptoInfo, sizes);
407                 break;
408             }
409             case 1: {
410                 size_t sizes[kMaxCryptoKey];
411                 AMediaCodecCryptoInfo_getClearBytes(cryptoInfo, sizes);
412                 break;
413             }
414             case 2: {
415                 uint8_t bytes[kMaxCryptoKey];
416                 AMediaCodecCryptoInfo_getIV(cryptoInfo, bytes);
417                 break;
418             }
419             case 3:
420             default: {
421                 uint8_t bytes[kMaxCryptoKey];
422                 AMediaCodecCryptoInfo_getKey(cryptoInfo, bytes);
423                 break;
424             }
425         }
426         AMediaCodecCryptoInfo_delete(cryptoInfo);
427     }
428 }
429 
process()430 void NdkAsyncCodecFuzzer::process() {
431     if (mFdp.ConsumeBool()) {
432         invokeCodecCryptoInfoAPI();
433     } else {
434         invokeAsyncCodeConfigAPI();
435     }
436 }
437 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)438 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
439     NdkAsyncCodecFuzzer ndkAsyncCodecFuzzer(data, size);
440     ndkAsyncCodecFuzzer.process();
441     return 0;
442 }
443