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