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