1 /*
2 * Copyright (C) 2020 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-base/properties.h>
18 #include <android-base/unique_fd.h>
19 #include <fuzzer/FuzzedDataProvider.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <filesystem>
25 #include <fstream>
26 #include <string>
27
28 #define LOG_TAG "MtpFuzzer"
29
30 #include "IMtpHandle.h"
31 #include "MtpMockDatabase.h"
32 #include "MtpMockHandle.h"
33 #include "MtpObjectInfo.h"
34 #include "MtpServer.h"
35 #include "MtpStorage.h"
36 #include "MtpUtils.h"
37
38 constexpr int32_t kMinFiles = 0;
39 constexpr int32_t kMaxFiles = 5;
40 constexpr int32_t kMaxBytes = 128;
41 constexpr float kMinDataSizeFactor = 0.8;
42 // prefer tmpfs for file operations to avoid wearing out flash
43 const char* storage_path = "/storage/fuzzer/0";
44 const char* source_database = "/data/local/tmp/srcdb/";
45 const std::string test_path = std::string(source_database) + "TestDir/";
46 const std::string kPropertyKey = "sys.fuse.transcode_mtp";
47
48 namespace android {
49 class MtpMockServer {
50 public:
MtpMockServer(const uint8_t * data,size_t size)51 MtpMockServer(const uint8_t* data, size_t size) : mFdp(data, size) {
52 // This is unused in our harness
53 int controlFd = -1;
54
55 mHandle = std::make_unique<MtpMockHandle>();
56 mStorage = std::make_unique<MtpStorage>(
57 mFdp.ConsumeIntegral<uint32_t>() /* storageId */, storage_path,
58 mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* descriptor */,
59 mFdp.ConsumeBool() /* removable */,
60 mFdp.ConsumeIntegral<uint64_t>() /* maxFileSize */);
61 mDatabase = std::make_unique<MtpMockDatabase>();
62 mDatabase->addStorage(mStorage.get());
63
64 init(data, size);
65
66 mMtp = std::make_unique<MtpServer>(
67 mDatabase.get(), controlFd, mFdp.ConsumeBool() /* ptp */,
68 mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* manu */,
69 mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* model */,
70 mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* version */,
71 mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* serial */);
72 mMtp->addStorage(mStorage.get());
73
74 // clear the old handle first, so we don't leak memory
75 delete mMtp->mHandle;
76 mMtp->mHandle = mHandle.get();
77 }
78
process()79 void process() {
80 if (mFdp.ConsumeBool()) {
81 createDatabaseFromSourceDir(source_database, storage_path, MTP_PARENT_ROOT);
82 }
83
84 while (mFdp.remaining_bytes()) {
85 MtpStorage storage(mFdp.ConsumeIntegral<uint32_t>() /* id */,
86 mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* filePath */,
87 mFdp.ConsumeRandomLengthString(kMaxBytes).c_str() /* description */,
88 mFdp.ConsumeBool() /* removable */,
89 mFdp.ConsumeIntegral<uint64_t>() /* maxFileSize */);
90
91 auto invokeMtpServerAPI = mFdp.PickValueInArray<const std::function<void()>>({
92 [&]() { mMtp->run(); },
93 [&]() { mMtp->sendObjectAdded(mFdp.ConsumeIntegral<uint32_t>()); },
94 [&]() { mMtp->sendObjectRemoved(mFdp.ConsumeIntegral<uint32_t>()); },
95 [&]() { mMtp->sendObjectInfoChanged(mFdp.ConsumeIntegral<uint32_t>()); },
96 [&]() { mMtp->sendDevicePropertyChanged(mFdp.ConsumeIntegral<uint16_t>()); },
97 [&]() { mMtp->addStorage(&storage); },
98 [&]() { mMtp->removeStorage(&storage); },
99 });
100
101 invokeMtpServerAPI();
102 }
103
104 std::filesystem::remove_all(source_database);
105 }
106
107 private:
createFiles(std::string path,size_t fileCount)108 void createFiles(std::string path, size_t fileCount) {
109 std::ofstream file;
110 for (size_t idx = 0; idx < fileCount; ++idx) {
111 file.open(path.append(std::to_string(idx)));
112 file.close();
113 }
114 }
115
addPackets(const uint8_t * data,size_t size)116 void addPackets(const uint8_t* data, size_t size) {
117 size_t off = 0;
118 for (size_t i = 0; i < size; ++i) {
119 // A longer delimiter could be used, but this worked in practice
120 if (data[i] == '@') {
121 size_t pktsz = i - off;
122 if (pktsz > 0) {
123 packet_t pkt = packet_t((unsigned char*)data + off, (unsigned char*)data + i);
124 // insert into packet buffer
125 mHandle->add_packet(pkt);
126 off = i;
127 }
128 }
129 }
130 }
131
init(const uint8_t * data,size_t size)132 void init(const uint8_t* data, size_t size) {
133 std::vector<uint8_t> packetData = mFdp.ConsumeBytes<uint8_t>(
134 mFdp.ConsumeIntegralInRange<int32_t>(kMinDataSizeFactor * size, size));
135
136 // Packetize the input stream
137 addPackets(packetData.data(), packetData.size());
138
139 // Setting the property to true/false to randomly fuzz the PoC depended on it
140 base::SetProperty(kPropertyKey, mFdp.ConsumeBool() ? "true" : "false");
141
142 std::filesystem::create_directories(source_database);
143 if (mFdp.ConsumeBool()) {
144 std::filesystem::create_directories(test_path);
145 createFiles(test_path, mFdp.ConsumeIntegralInRange<size_t>(kMinFiles, kMaxFiles));
146 }
147 createFiles(source_database, mFdp.ConsumeIntegralInRange<size_t>(kMinFiles, kMaxFiles));
148 }
149
createDatabaseFromSourceDir(const char * fromPath,const char * toPath,MtpObjectHandle parentHandle)150 int createDatabaseFromSourceDir(const char* fromPath, const char* toPath,
151 MtpObjectHandle parentHandle) {
152 int ret = 0;
153 std::string fromPathStr(fromPath);
154 std::string toPathStr(toPath);
155
156 DIR* dir = opendir(fromPath);
157 if (!dir) {
158 ALOGE("opendir %s failed", fromPath);
159 return -1;
160 }
161 if (fromPathStr[fromPathStr.size() - 1] != '/') fromPathStr += '/';
162 if (toPathStr[toPathStr.size() - 1] != '/') toPathStr += '/';
163
164 struct dirent* entry;
165 while ((entry = readdir(dir))) {
166 const char* name = entry->d_name;
167
168 // ignore "." and ".."
169 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
170 continue;
171 }
172
173 std::string oldFile = fromPathStr + name;
174 std::string newFile = toPathStr + name;
175
176 if (entry->d_type == DT_DIR) {
177 ret += makeFolder(newFile.c_str());
178
179 MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
180 objectInfo->mStorageID = mStorage->getStorageID();
181 objectInfo->mParent = parentHandle;
182 objectInfo->mFormat = MTP_FORMAT_ASSOCIATION; // folder
183 objectInfo->mName = strdup(name);
184 objectInfo->mKeywords = strdup("");
185
186 mDatabase->addObject(objectInfo);
187
188 ret += createDatabaseFromSourceDir(oldFile.c_str(), newFile.c_str(),
189 objectInfo->mHandle);
190 } else {
191 ret += copyFile(oldFile.c_str(), newFile.c_str());
192
193 MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
194 objectInfo->mStorageID = mStorage->getStorageID();
195 objectInfo->mParent = parentHandle;
196 objectInfo->mFormat = MTP_FORMAT_TEXT;
197 objectInfo->mName = strdup(name);
198 objectInfo->mKeywords = strdup("");
199
200 mDatabase->addObject(objectInfo);
201 }
202 }
203
204 closedir(dir);
205 return ret;
206 }
207
208 FuzzedDataProvider mFdp;
209 std::unique_ptr<MtpMockHandle> mHandle;
210 std::unique_ptr<MtpStorage> mStorage;
211 std::unique_ptr<MtpMockDatabase> mDatabase;
212 std::unique_ptr<MtpServer> mMtp;
213 };
214 }; // namespace android
215
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)216 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) __attribute__((optnone)) {
217 // reset our storage (from MtpUtils.h)
218 android::deletePath(storage_path);
219 android::makeFolder("/storage/fuzzer");
220 android::makeFolder(storage_path);
221
222 std::unique_ptr<android::MtpMockServer> mtp =
223 std::make_unique<android::MtpMockServer>(data, size);
224 mtp->process();
225
226 std::filesystem::remove_all("/storage/fuzzer");
227 return 0;
228 }
229