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/unique_fd.h>
18 #include <stddef.h>
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 
23 #include <string>
24 
25 #define LOG_TAG "MtpFuzzer"
26 
27 #include "IMtpHandle.h"
28 #include "MtpMockDatabase.h"
29 #include "MtpMockHandle.h"
30 #include "MtpObjectInfo.h"
31 #include "MtpServer.h"
32 #include "MtpStorage.h"
33 #include "MtpUtils.h"
34 
35 const char* storage_desc = "Fuzz Storage";
36 // prefer tmpfs for file operations to avoid wearing out flash
37 const char* storage_path = "/storage/fuzzer/0";
38 const char* source_database = "srcdb/";
39 
40 namespace android {
41 class MtpMockServer {
42 public:
43     std::unique_ptr<MtpMockHandle> mHandle;
44     std::unique_ptr<MtpStorage> mStorage;
45     std::unique_ptr<MtpMockDatabase> mDatabase;
46     std::unique_ptr<MtpServer> mMtp;
47     int mStorageId;
48 
MtpMockServer(const char * storage_path)49     MtpMockServer(const char* storage_path) : mStorageId(0) {
50         bool ptp = false;
51         const char* manu = "Google";
52         const char* model = "Pixel 3XL";
53         const char* version = "1.0";
54         const char* serial = "ABDEF1231";
55 
56         // This is unused in our harness
57         int controlFd = -1;
58 
59         mHandle = std::make_unique<MtpMockHandle>();
60         mStorage = std::make_unique<MtpStorage>(mStorageId, storage_path, storage_desc, true,
61                                                 0x200000000L);
62         mDatabase = std::make_unique<MtpMockDatabase>();
63         mDatabase->addStorage(mStorage.get());
64 
65         mMtp = std::make_unique<MtpServer>(mDatabase.get(), controlFd, ptp, manu, model, version,
66                                            serial);
67         mMtp->addStorage(mStorage.get());
68 
69         // clear the old handle first, so we don't leak memory
70         delete mMtp->mHandle;
71         mMtp->mHandle = mHandle.get();
72     }
73 
run()74     void run() { mMtp->run(); }
75 
createDatabaseFromSourceDir(const char * fromPath,const char * toPath,MtpObjectHandle parentHandle)76     int createDatabaseFromSourceDir(const char* fromPath, const char* toPath,
77                                     MtpObjectHandle parentHandle) {
78         int ret = 0;
79         std::string fromPathStr(fromPath);
80         std::string toPathStr(toPath);
81 
82         DIR* dir = opendir(fromPath);
83         if (!dir) {
84             ALOGE("opendir %s failed", fromPath);
85             return -1;
86         }
87         if (fromPathStr[fromPathStr.size() - 1] != '/') fromPathStr += '/';
88         if (toPathStr[toPathStr.size() - 1] != '/') toPathStr += '/';
89 
90         struct dirent* entry;
91         while ((entry = readdir(dir))) {
92             const char* name = entry->d_name;
93 
94             // ignore "." and ".."
95             if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
96                 continue;
97             }
98 
99             std::string oldFile = fromPathStr + name;
100             std::string newFile = toPathStr + name;
101 
102             if (entry->d_type == DT_DIR) {
103                 ret += makeFolder(newFile.c_str());
104 
105                 MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
106                 objectInfo->mStorageID = mStorage->getStorageID();
107                 objectInfo->mParent = parentHandle;
108                 objectInfo->mFormat = MTP_FORMAT_ASSOCIATION; // folder
109                 objectInfo->mName = strdup(name);
110                 objectInfo->mKeywords = strdup("");
111 
112                 mDatabase->addObject(objectInfo);
113 
114                 ret += createDatabaseFromSourceDir(oldFile.c_str(), newFile.c_str(),
115                                                    objectInfo->mHandle);
116             } else {
117                 ret += copyFile(oldFile.c_str(), newFile.c_str());
118 
119                 MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
120                 objectInfo->mStorageID = mStorage->getStorageID();
121                 objectInfo->mParent = parentHandle;
122                 objectInfo->mFormat = MTP_FORMAT_TEXT;
123                 objectInfo->mName = strdup(name);
124                 objectInfo->mKeywords = strdup("");
125 
126                 mDatabase->addObject(objectInfo);
127             }
128         }
129 
130         closedir(dir);
131         return ret;
132     }
133 };
134 }; // namespace android
135 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)136 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) __attribute__((optnone)) {
137     // reset our storage (from MtpUtils.h)
138     android::deletePath(storage_path);
139     android::makeFolder("/storage/fuzzer");
140     android::makeFolder(storage_path);
141 
142     std::unique_ptr<android::MtpMockServer> mtp =
143             std::make_unique<android::MtpMockServer>(storage_path);
144 
145     size_t off = 0;
146 
147     // Packetize the input stream
148     for (size_t i = 0; i < size; i++) {
149         // A longer delimiter could be used, but this worked in practice
150         if (data[i] == '@') {
151             size_t pktsz = i - off;
152             if (pktsz > 0) {
153                 packet_t pkt = packet_t((unsigned char*)data + off, (unsigned char*)data + i);
154                 // insert into packet buffer
155                 mtp->mHandle->add_packet(pkt);
156                 off = i;
157             }
158         }
159     }
160 
161     mtp->createDatabaseFromSourceDir(source_database, storage_path, MTP_PARENT_ROOT);
162     mtp->run();
163 
164     return 0;
165 }
166