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 
17 #include <android/binder_process.h>
18 #include <fuzzer/FuzzedDataProvider.h>
19 #include <media/NdkMediaExtractor.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 
23 constexpr int32_t kCaseStart = 0;
24 constexpr int32_t kCaseEnd = 8;
25 constexpr float kMinDataSizeFactor = 0.5;
26 constexpr int32_t kMaxIterations = 1000;
27 const std::string kPathPrefix = "file://";
28 
29 constexpr SeekMode kSeekMode[] = {AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC,
30                                   AMEDIAEXTRACTOR_SEEK_NEXT_SYNC,
31                                   AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC};
32 
33 class NdkExtractorFuzzer {
34   public:
NdkExtractorFuzzer(const uint8_t * data,size_t size)35     NdkExtractorFuzzer(const uint8_t* data, size_t size) : mFdp(data, size) {
36         mDataSourceFd = mkstemp(mTestPath);
37         std::vector<char> dataBuffer = mFdp.ConsumeBytes<char>(
38                 mFdp.ConsumeIntegralInRange<int32_t>(kMinDataSizeFactor * size, size));
39         mDataSize = dataBuffer.size();
40         write(mDataSourceFd, dataBuffer.data(), dataBuffer.size());
41     };
42 
~NdkExtractorFuzzer()43     ~NdkExtractorFuzzer() {
44         close(mDataSourceFd);
45         remove(mTestPath);
46     };
47 
48     void process();
49 
50   private:
51     FuzzedDataProvider mFdp;
52     int32_t mDataSourceFd = 0;
53     int32_t mDataSize = 0;
54 
55     // Defined a mutable TestSource file path for mkstemp().
56     char mTestPath[64] = "/data/local/tmp/TestSource_XXXXXX";
57 };
58 
process()59 void NdkExtractorFuzzer::process() {
60     AMediaExtractor* mMediaExtractor = AMediaExtractor_new();
61     AMediaDataSource* mDataSource = nullptr;
62 
63     if (mFdp.ConsumeBool()) {
64         AMediaExtractor_setDataSourceFd(mMediaExtractor, mDataSourceFd, 0, mDataSize);
65     } else {
66         mDataSource = AMediaDataSource_newUri((kPathPrefix + mTestPath).c_str(), 0 /* numkeys */,
67                                               nullptr /* keyvalues */);
68         AMediaExtractor_setDataSourceCustom(mMediaExtractor, mDataSource);
69     }
70 
71     /**
72      * Limiting the number of iterations of while loop
73      * to prevent a possible timeout.
74      */
75     int32_t count = 0;
76     while (mFdp.remaining_bytes() && count++ < kMaxIterations) {
77         switch (mFdp.ConsumeIntegralInRange<int32_t>(kCaseStart, kCaseEnd)) {
78             case 0:{
79                 AMediaExtractor_selectTrack(mMediaExtractor,
80                                             mFdp.ConsumeIntegral<size_t>() /* idx */);
81                 break;
82             }
83             case 1:{
84                 AMediaExtractor_unselectTrack(mMediaExtractor,
85                                               mFdp.ConsumeIntegral<size_t>() /* idx */);
86                 break;
87             }
88             case 2:{
89                 int32_t sampleSize = AMediaExtractor_getSampleSize(mMediaExtractor);
90                 if (sampleSize > 0) {
91                     std::vector<uint8_t> buffer(sampleSize);
92                     AMediaExtractor_readSampleData(
93                             mMediaExtractor, buffer.data(),
94                             mFdp.ConsumeIntegralInRange<size_t>(0, sampleSize) /* capacity */);
95                 }
96                 break;
97             }
98             case 3:{
99                 AMediaExtractor_getSampleFlags(mMediaExtractor);
100                 break;
101             }
102             case 4:{
103                 AMediaExtractor_getSampleCryptoInfo(mMediaExtractor);
104                 break;
105             }
106             case 5:{
107                 AMediaExtractor_getPsshInfo(mMediaExtractor);
108                 break;
109             }
110             case 6:{
111                 AMediaExtractor_advance(mMediaExtractor);
112                 break;
113             }
114             case 7:{
115                 AMediaFormat* mediaFormat = mFdp.ConsumeBool() ? AMediaFormat_new() : nullptr;
116                 AMediaExtractor_getSampleFormat(mMediaExtractor, mediaFormat);
117                 AMediaFormat_delete(mediaFormat);
118                 break;
119             }
120             case 8:{
121                 AMediaExtractor_seekTo(mMediaExtractor,
122                                        mFdp.ConsumeIntegral<int64_t>() /* seekPosUs */,
123                                        mFdp.PickValueInArray(kSeekMode) /* mode */);
124                 break;
125             }
126         };
127     }
128 
129     AMediaDataSource_delete(mDataSource);
130     AMediaExtractor_delete(mMediaExtractor);
131 }
132 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)133 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
134     /**
135      * Create a threadpool for incoming binder transactions,
136      * without this extractor results in a DoS after few instances.
137      */
138     ABinderProcess_startThreadPool();
139 
140     NdkExtractorFuzzer ndkExtractorFuzzer(data, size);
141     ndkExtractorFuzzer.process();
142     return 0;
143 }
144