xref: /aosp_15_r20/frameworks/av/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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