MTP: Add fuzzer dict & corpus loader

Moved and modified from libFuzzer/mtp_fuzzer

Bug: 140904260
Test: mtp_fuzzer -dict=mtp_fuzzer.dict corpus
Change-Id: I8e063c9b4eaa2b834fc8759e43865d81cea3d8c5
diff --git a/media/mtp/tests/MtpFuzzer/Android.bp b/media/mtp/tests/MtpFuzzer/Android.bp
new file mode 100644
index 0000000..9cd4669
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/Android.bp
@@ -0,0 +1,31 @@
+cc_fuzz {
+    name: "mtp_fuzzer",
+    srcs: [
+        "mtp_fuzzer.cpp",
+        "MtpMockDatabase.cpp",
+    ],
+    shared_libs: [
+	"libmtp",
+	"libbase",
+	"liblog",
+	"libutils",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-DMTP_DEVICE",
+        "-Wno-unused-parameter",
+    ],
+    dictionary: "mtp_fuzzer.dict",
+    corpus: ["corpus/*"],
+
+    fuzz_config: {
+
+        cc: ["jameswei@google.com"],
+        componentid: 1344,
+        acknowledgement: [
+            "Grant Hernandez of Google",
+        ],
+    },
+}
diff --git a/media/mtp/tests/MtpFuzzer/MtpMockDatabase.cpp b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.cpp
new file mode 100644
index 0000000..5d95aa2
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <string>
+
+#define LOG_TAG "MtpFuzzer"
+
+#include <log/log.h>
+
+#include "MtpDebug.h"
+#include "MtpMockDatabase.h"
+#include "MtpObjectInfo.h"
+
+namespace android {
+
+MtpMockDatabase::MtpMockDatabase() : mLastObjectHandle(0) {}
+
+MtpMockDatabase::~MtpMockDatabase() {
+    for (MtpObjectInfo* i : mObjects) {
+        delete i;
+    }
+    mObjects.clear();
+}
+
+void MtpMockDatabase::addObject(MtpObjectInfo* info) {
+    assert(hasStorage(info->storageID));
+
+    // we take ownership
+    mObjects.push_back(info);
+
+    return;
+}
+
+MtpObjectHandle MtpMockDatabase::allocateObjectHandle() {
+    // this is in sync with our mObjects database
+    return mLastObjectHandle++;
+}
+
+// Called from SendObjectInfo to reserve a database entry for the incoming file.
+MtpObjectHandle MtpMockDatabase::beginSendObject(const char* path, MtpObjectFormat format,
+                                                 MtpObjectHandle parent, MtpStorageID storage) {
+    if (!hasStorage(storage)) {
+        ALOGW("%s: Tried to lookup storageID %u, but doesn't exist\n", __func__, storage);
+        return kInvalidObjectHandle;
+    }
+
+    ALOGD("MockDatabase %s: path=%s oformat=0x%04x parent_handle=%u "
+          "storage_id=%u\n",
+          __func__, path, format, parent, storage);
+
+    return mLastObjectHandle;
+}
+
+// Called to report success or failure of the SendObject file transfer.
+void MtpMockDatabase::endSendObject(MtpObjectHandle handle, bool succeeded) {
+    ALOGD("MockDatabase %s: ohandle=%u succeeded=%d\n", __func__, handle, succeeded);
+}
+
+// Called to rescan a file, such as after an edit.
+void MtpMockDatabase::rescanFile(const char* path, MtpObjectHandle handle, MtpObjectFormat format) {
+    ALOGD("MockDatabase %s: path=%s ohandle=%u, oformat=0x%04x\n", __func__, path, handle, format);
+}
+
+MtpObjectHandleList* MtpMockDatabase::getObjectList(MtpStorageID storageID, MtpObjectFormat format,
+                                                    MtpObjectHandle parent) {
+    ALOGD("MockDatabase %s: storage_id=%u oformat=0x%04x ohandle=%u\n", __func__, storageID, format,
+          parent);
+    return nullptr;
+}
+
+int MtpMockDatabase::getNumObjects(MtpStorageID storageID, MtpObjectFormat format,
+                                   MtpObjectHandle parent) {
+    ALOGD("MockDatabase %s: storage_id=%u oformat=0x%04x ohandle=%u\n", __func__, storageID, format,
+          parent);
+    // TODO: return MTP_RESPONSE_OK when it stops segfaulting
+    return 0;
+}
+
+// callee should delete[] the results from these
+// results can be NULL
+MtpObjectFormatList* MtpMockDatabase::getSupportedPlaybackFormats() {
+    ALOGD("MockDatabase %s\n", __func__);
+    return nullptr;
+}
+MtpObjectFormatList* MtpMockDatabase::getSupportedCaptureFormats() {
+    ALOGD("MockDatabase %s\n", __func__);
+    return nullptr;
+}
+MtpObjectPropertyList* MtpMockDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+    ALOGD("MockDatabase %s: oformat=0x%04x\n", __func__, format);
+    return nullptr;
+}
+MtpDevicePropertyList* MtpMockDatabase::getSupportedDeviceProperties() {
+    ALOGD("MockDatabase %s\n", __func__);
+    return nullptr;
+}
+
+MtpResponseCode MtpMockDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+                                                        MtpObjectProperty property,
+                                                        MtpDataPacket& packet) {
+    ALOGD("MockDatabase %s: ohandle=%u property=%s\n", __func__, handle,
+          MtpDebug::getObjectPropCodeName(property));
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+                                                        MtpObjectProperty property,
+                                                        MtpDataPacket& packet) {
+    ALOGD("MockDatabase %s: ohandle=%u property=%s\n", __func__, handle,
+          MtpDebug::getObjectPropCodeName(property));
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+                                                        MtpDataPacket& packet) {
+    ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::setDevicePropertyValue(MtpDeviceProperty property,
+                                                        MtpDataPacket& packet) {
+    ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+    ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::getObjectPropertyList(MtpObjectHandle handle, uint32_t format,
+                                                       uint32_t property, int groupCode, int depth,
+                                                       MtpDataPacket& packet) {
+    ALOGD("MockDatabase %s: ohandle=%u format=%s property=%s groupCode=%d "
+          "depth=%d\n",
+          __func__, handle, MtpDebug::getFormatCodeName(format),
+          MtpDebug::getObjectPropCodeName(property), groupCode, depth);
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+    ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+
+    // used for the root
+    if (handle == kInvalidObjectHandle) {
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+    } else {
+        if (mObjects.size() == 0) {
+            return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+        }
+
+        // this is used to let the fuzzer make progress, otherwise
+        // it has to brute-force a 32-bit handle
+        MtpObjectHandle reducedHandle = handle % mObjects.size();
+        MtpObjectInfo* obj = mObjects[reducedHandle];
+
+        // make a copy, but make sure to maintain ownership of string pointers
+        info = *obj;
+
+        // fixup the response handle
+        info.mHandle = handle;
+
+        if (obj->mName) info.mName = strdup(obj->mName);
+        if (obj->mKeywords) info.mKeywords = strdup(obj->mKeywords);
+
+        return MTP_RESPONSE_OK;
+    }
+}
+
+void* MtpMockDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
+    ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+
+    size_t allocSize = handle % 0x1000;
+    void* data = calloc(allocSize, sizeof(uint8_t));
+    if (!data) {
+        return nullptr;
+    } else {
+        ALOGD("MockDatabase %s\n", __func__);
+        outThumbSize = allocSize;
+        return data;
+    }
+}
+
+MtpResponseCode MtpMockDatabase::getObjectFilePath(MtpObjectHandle handle,
+                                                   MtpStringBuffer& outFilePath,
+                                                   int64_t& outFileLength,
+                                                   MtpObjectFormat& outFormat) {
+    ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+
+    if (mObjects.size() == 0) {
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+    }
+
+    // this is used to let the fuzzer make progress, otherwise
+    // it has to brute-force a 32-bit handle
+    MtpObjectHandle reducedHandle = handle % mObjects.size();
+    MtpObjectInfo* obj = mObjects[reducedHandle];
+    MtpStorage* storage = mStorage[obj->mStorageID];
+
+    // walk up the tree to build a full path of the object
+    MtpObjectHandle currentHandle = reducedHandle;
+    std::string path = "";
+
+    while (currentHandle != MTP_PARENT_ROOT) {
+        MtpObjectInfo* next = mObjects[currentHandle];
+
+        // prepend the name
+        if (path == "")
+            path = std::string(next->mName);
+        else
+            path = std::string(next->mName) + "/" + path;
+
+        currentHandle = next->mParent;
+    }
+
+    outFilePath.set(storage->getPath());
+    outFilePath.append("/");
+    outFilePath.append(path.c_str());
+
+    outFormat = obj->mFormat;
+
+    ALOGD("MockDatabase %s: get file %s\n", __func__, (const char*)outFilePath);
+
+    struct stat sstat;
+    // this should not happen unless our database view of the filesystem is out of
+    // sync
+    if (stat((const char*)outFilePath, &sstat) < 0) {
+        ALOGE("MockDatabase %s: unable to stat %s\n", __func__, (const char*)outFilePath);
+
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+    }
+
+    outFileLength = sstat.st_size;
+
+    return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpMockDatabase::beginDeleteObject(MtpObjectHandle handle) {
+    ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+    return MTP_RESPONSE_OK;
+}
+void MtpMockDatabase::endDeleteObject(MtpObjectHandle handle, bool succeeded) {
+    ALOGD("MockDatabase %s: ohandle=%u succeeded=%d\n", __func__, handle, succeeded);
+    return;
+}
+
+MtpObjectHandleList* MtpMockDatabase::getObjectReferences(MtpObjectHandle handle) {
+    ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+    return nullptr;
+}
+
+MtpResponseCode MtpMockDatabase::setObjectReferences(MtpObjectHandle handle,
+                                                     MtpObjectHandleList* references) {
+    ALOGD("MockDatabase %s: ohandle=%u\n", __func__, handle);
+    return MTP_RESPONSE_OK;
+}
+
+MtpProperty* MtpMockDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+                                                    MtpObjectFormat format) {
+    ALOGD("MockDatabase %s: property=%s format=%s\n", __func__,
+          MtpDebug::getObjectPropCodeName(property), MtpDebug::getFormatCodeName(format));
+
+    return nullptr;
+}
+
+MtpProperty* MtpMockDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+    ALOGD("MockDatabase %s: property=%s\n", __func__, MtpDebug::getDevicePropCodeName(property));
+    return nullptr;
+}
+
+MtpResponseCode MtpMockDatabase::beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+                                                 MtpStorageID newStorage) {
+    ALOGD("MockDatabase %s: ohandle=%u newParent=%u newStorage=%u\n", __func__, handle, newParent,
+          newStorage);
+    return MTP_RESPONSE_OK;
+}
+
+void MtpMockDatabase::endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
+                                    MtpStorageID oldStorage, MtpStorageID newStorage,
+                                    MtpObjectHandle handle, bool succeeded) {
+    ALOGD("MockDatabase %s: oldParent=%u newParent=%u oldStorage=%u newStorage=%u "
+          "ohandle=%u succeeded=%d\n",
+          __func__, oldParent, newParent, oldStorage, newStorage, handle, succeeded);
+    return;
+}
+
+MtpResponseCode MtpMockDatabase::beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+                                                 MtpStorageID newStorage) {
+    ALOGD("MockDatabase %s: ohandle=%u newParent=%u newStorage=%u\n", __func__, handle, newParent,
+          newStorage);
+    return MTP_RESPONSE_OK;
+}
+
+void MtpMockDatabase::endCopyObject(MtpObjectHandle handle, bool succeeded) {
+    ALOGD("MockDatabase %s: ohandle=%u succeeded=%d\n", __func__, handle, succeeded);
+}
+
+}; // namespace android
diff --git a/media/mtp/tests/MtpFuzzer/MtpMockDatabase.h b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.h
new file mode 100644
index 0000000..876719e
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/MtpMockDatabase.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _MTP_MOCK_DATABASE_H
+#define _MTP_MOCK_DATABASE_H
+
+#include <map>
+
+#include "IMtpDatabase.h"
+#include "MtpStorage.h"
+
+namespace android {
+
+class MtpMockDatabase : public IMtpDatabase {
+    std::map<MtpStorageID, MtpStorage*> mStorage;
+    std::vector<MtpObjectInfo*> mObjects;
+    uint32_t mLastObjectHandle;
+
+public:
+    MtpMockDatabase();
+    virtual ~MtpMockDatabase();
+
+    // MtpFuzzer methods
+    void addStorage(MtpStorage* storage) {
+        // we don't own this
+        mStorage[storage->getStorageID()] = storage;
+    }
+
+    bool hasStorage(MtpStorageID storage) { return mStorage.find(storage) != mStorage.end(); }
+
+    void addObject(MtpObjectInfo* info);
+    MtpObjectHandle allocateObjectHandle();
+
+    // libmtp interface methods
+    // Called from SendObjectInfo to reserve a database entry for the incoming
+    // file.
+    MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format,
+                                    MtpObjectHandle parent, MtpStorageID storage);
+
+    // Called to report success or failure of the SendObject file transfer.
+    void endSendObject(MtpObjectHandle handle, bool succeeded);
+
+    // Called to rescan a file, such as after an edit.
+    void rescanFile(const char* path, MtpObjectHandle handle, MtpObjectFormat format);
+
+    MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectFormat format,
+                                       MtpObjectHandle parent);
+
+    int getNumObjects(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
+
+    // callee should delete[] the results from these
+    // results can be NULL
+    MtpObjectFormatList* getSupportedPlaybackFormats();
+    MtpObjectFormatList* getSupportedCaptureFormats();
+    MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format);
+    MtpDevicePropertyList* getSupportedDeviceProperties();
+
+    MtpResponseCode getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property,
+                                           MtpDataPacket& packet);
+
+    MtpResponseCode setObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property,
+                                           MtpDataPacket& packet);
+
+    MtpResponseCode getDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet);
+
+    MtpResponseCode setDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet);
+
+    MtpResponseCode resetDeviceProperty(MtpDeviceProperty property);
+
+    MtpResponseCode getObjectPropertyList(MtpObjectHandle handle, uint32_t format,
+                                          uint32_t property, int groupCode, int depth,
+                                          MtpDataPacket& packet);
+
+    MtpResponseCode getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info);
+
+    void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
+
+    MtpResponseCode getObjectFilePath(MtpObjectHandle handle, MtpStringBuffer& outFilePath,
+                                      int64_t& outFileLength, MtpObjectFormat& outFormat);
+
+    MtpResponseCode beginDeleteObject(MtpObjectHandle handle);
+    void endDeleteObject(MtpObjectHandle handle, bool succeeded);
+
+    MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle);
+
+    MtpResponseCode setObjectReferences(MtpObjectHandle handle, MtpObjectHandleList* references);
+
+    MtpProperty* getObjectPropertyDesc(MtpObjectProperty property, MtpObjectFormat format);
+
+    MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property);
+
+    MtpResponseCode beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+                                    MtpStorageID newStorage);
+
+    void endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
+                       MtpStorageID oldStorage, MtpStorageID newStorage, MtpObjectHandle handle,
+                       bool succeeded);
+
+    MtpResponseCode beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+                                    MtpStorageID newStorage);
+    void endCopyObject(MtpObjectHandle handle, bool succeeded);
+};
+
+}; // namespace android
+
+#endif // _MTP_MOCK_DATABASE_H
diff --git a/media/mtp/tests/MtpFuzzer/MtpMockHandle.h b/media/mtp/tests/MtpFuzzer/MtpMockHandle.h
new file mode 100644
index 0000000..111485c
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/MtpMockHandle.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_MOCK_HANDLE_H
+#define _MTP_MOCK_HANDLE_H
+
+#include <vector>
+
+typedef std::vector<uint8_t> packet_t;
+
+namespace android {
+class MtpMockHandle : public IMtpHandle {
+private:
+    size_t mPacketNumber;
+    size_t mPacketOffset;
+    std::vector<packet_t> mPackets;
+
+public:
+    MtpMockHandle() : mPacketNumber(0), mPacketOffset(0) {}
+
+    void add_packet(packet_t pkt) { mPackets.push_back(pkt); }
+
+    // Return number of bytes read/written, or -1 and errno is set
+    int read(void *data, size_t len) {
+        if (mPacketNumber >= mPackets.size()) {
+            return 0;
+        } else {
+            int readAmt = 0;
+            packet_t pkt = mPackets[mPacketNumber];
+
+            ALOGD("%s: sz %zu, pkt %zu+%zu/%zu\n", __func__, len, mPacketNumber, mPacketOffset,
+                  pkt.size());
+
+            // packet is bigger than what the caller can handle,
+            if (pkt.size() > len) {
+                memcpy(data, pkt.data() + mPacketOffset, len);
+
+                mPacketOffset += len;
+                readAmt = len;
+                // packet is equal or smaller than the caller buffer
+            } else {
+                memcpy(data, pkt.data() + mPacketOffset, pkt.size());
+
+                mPacketNumber++;
+                mPacketOffset = 0;
+                readAmt = pkt.size();
+            }
+
+            return readAmt;
+        }
+    }
+    int write(const void *data, size_t len) {
+        ALOGD("MockHandle %s: len=%zu\n", __func__, len);
+        // fake the write
+        return len;
+    }
+
+    // Return 0 if send/receive is successful, or -1 and errno is set
+    int receiveFile(mtp_file_range mfr, bool zero_packet) {
+        ALOGD("MockHandle %s\n", __func__);
+        return 0;
+    }
+    int sendFile(mtp_file_range mfr) {
+        ALOGD("MockHandle %s\n", __func__);
+        return 0;
+    }
+    int sendEvent(mtp_event me) {
+        ALOGD("MockHandle %s: len=%zu\n", __func__, me.length);
+        return 0;
+    }
+
+    // Return 0 if operation is successful, or -1 else
+    int start(bool ptp) { return 0; }
+
+    void close() {}
+
+    virtual ~MtpMockHandle() {}
+};
+}; // namespace android
+
+#endif // _MTP_MOCK_HANDLE_H
diff --git a/media/mtp/tests/MtpFuzzer/corpus/1-mtp-open_session.pkt b/media/mtp/tests/MtpFuzzer/corpus/1-mtp-open_session.pkt
new file mode 100644
index 0000000..38f8ed2
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/1-mtp-open_session.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/2-mtp-get_device_info.pkt b/media/mtp/tests/MtpFuzzer/corpus/2-mtp-get_device_info.pkt
new file mode 100644
index 0000000..7759380
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/2-mtp-get_device_info.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/3-mtp-get_object_handles.pkt b/media/mtp/tests/MtpFuzzer/corpus/3-mtp-get_object_handles.pkt
new file mode 100644
index 0000000..e88410f
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/3-mtp-get_object_handles.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/4-mtp-get_object_info.pkt b/media/mtp/tests/MtpFuzzer/corpus/4-mtp-get_object_info.pkt
new file mode 100644
index 0000000..e283fb4
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/4-mtp-get_object_info.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/corpus/5-mtp-send_object_info.pkt b/media/mtp/tests/MtpFuzzer/corpus/5-mtp-send_object_info.pkt
new file mode 100644
index 0000000..7627f88
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/corpus/5-mtp-send_object_info.pkt
Binary files differ
diff --git a/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp
new file mode 100644
index 0000000..f578462
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/unique_fd.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <string>
+
+#define LOG_TAG "MtpFuzzer"
+
+#include "IMtpHandle.h"
+#include "MtpMockDatabase.h"
+#include "MtpMockHandle.h"
+#include "MtpObjectInfo.h"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpUtils.h"
+
+const char* storage_desc = "Fuzz Storage";
+// prefer tmpfs for file operations to avoid wearing out flash
+const char* storage_path = "/storage/fuzzer/0";
+const char* source_database = "srcdb/";
+
+namespace android {
+class MtpMockServer {
+public:
+    std::unique_ptr<MtpMockHandle> mHandle;
+    std::unique_ptr<MtpStorage> mStorage;
+    std::unique_ptr<MtpMockDatabase> mDatabase;
+    std::unique_ptr<MtpServer> mMtp;
+    int mStorageId;
+
+    MtpMockServer(const char* storage_path) : mStorageId(0) {
+        bool ptp = false;
+        const char* manu = "Google";
+        const char* model = "Pixel 3XL";
+        const char* version = "1.0";
+        const char* serial = "ABDEF1231";
+
+        // This is unused in our harness
+        int controlFd = -1;
+
+        mHandle = std::make_unique<MtpMockHandle>();
+        mStorage = std::make_unique<MtpStorage>(mStorageId, storage_path, storage_desc, true,
+                                                0x200000000L);
+        mDatabase = std::make_unique<MtpMockDatabase>();
+        mDatabase->addStorage(mStorage.get());
+
+        mMtp = std::make_unique<MtpServer>(mDatabase.get(), controlFd, ptp, manu, model, version,
+                                           serial);
+        mMtp->addStorage(mStorage.get());
+
+        // clear the old handle first, so we don't leak memory
+        delete mMtp->mHandle;
+        mMtp->mHandle = mHandle.get();
+    }
+
+    void run() { mMtp->run(); }
+
+    int createDatabaseFromSourceDir(const char* fromPath, const char* toPath,
+                                    MtpObjectHandle parentHandle) {
+        int ret = 0;
+        std::string fromPathStr(fromPath);
+        std::string toPathStr(toPath);
+
+        DIR* dir = opendir(fromPath);
+        if (!dir) {
+            ALOGE("opendir %s failed", fromPath);
+            return -1;
+        }
+        if (fromPathStr[fromPathStr.size() - 1] != '/') fromPathStr += '/';
+        if (toPathStr[toPathStr.size() - 1] != '/') toPathStr += '/';
+
+        struct dirent* entry;
+        while ((entry = readdir(dir))) {
+            const char* name = entry->d_name;
+
+            // ignore "." and ".."
+            if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+                continue;
+            }
+
+            std::string oldFile = fromPathStr + name;
+            std::string newFile = toPathStr + name;
+
+            if (entry->d_type == DT_DIR) {
+                ret += makeFolder(newFile.c_str());
+
+                MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
+                objectInfo->mStorageID = mStorage->getStorageID();
+                objectInfo->mParent = parentHandle;
+                objectInfo->mFormat = MTP_FORMAT_ASSOCIATION; // folder
+                objectInfo->mName = strdup(name);
+                objectInfo->mKeywords = strdup("");
+
+                mDatabase->addObject(objectInfo);
+
+                ret += createDatabaseFromSourceDir(oldFile.c_str(), newFile.c_str(),
+                                                   objectInfo->mHandle);
+            } else {
+                ret += copyFile(oldFile.c_str(), newFile.c_str());
+
+                MtpObjectInfo* objectInfo = new MtpObjectInfo(mDatabase->allocateObjectHandle());
+                objectInfo->mStorageID = mStorage->getStorageID();
+                objectInfo->mParent = parentHandle;
+                objectInfo->mFormat = MTP_FORMAT_TEXT;
+                objectInfo->mName = strdup(name);
+                objectInfo->mKeywords = strdup("");
+
+                mDatabase->addObject(objectInfo);
+            }
+        }
+
+        closedir(dir);
+        return ret;
+    }
+};
+}; // namespace android
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) __attribute__((optnone)) {
+    // reset our storage (from MtpUtils.h)
+    android::deletePath(storage_path);
+    android::makeFolder("/storage/fuzzer");
+    android::makeFolder(storage_path);
+
+    std::unique_ptr<android::MtpMockServer> mtp =
+            std::make_unique<android::MtpMockServer>(storage_path);
+
+    size_t off = 0;
+
+    // Packetize the input stream
+    for (size_t i = 0; i < size; i++) {
+        // A longer delimiter could be used, but this worked in practice
+        if (data[i] == '@') {
+            size_t pktsz = i - off;
+            if (pktsz > 0) {
+                packet_t pkt = packet_t((unsigned char*)data + off, (unsigned char*)data + i);
+                // insert into packet buffer
+                mtp->mHandle->add_packet(pkt);
+                off = i;
+            }
+        }
+    }
+
+    mtp->createDatabaseFromSourceDir(source_database, storage_path, MTP_PARENT_ROOT);
+    mtp->run();
+
+    return 0;
+}
diff --git a/media/mtp/tests/MtpFuzzer/mtp_fuzzer.dict b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.dict
new file mode 100644
index 0000000..4c3f136
--- /dev/null
+++ b/media/mtp/tests/MtpFuzzer/mtp_fuzzer.dict
@@ -0,0 +1,74 @@
+mtp_operation_get_device_info="\x01\x10"
+mtp_operation_open_session="\x02\x10"
+mtp_operation_close_session="\x03\x10"
+mtp_operation_get_storage_ids="\x04\x10"
+mtp_operation_get_storage_info="\x05\x10"
+mtp_operation_get_num_objects="\x06\x10"
+mtp_operation_get_object_handles="\x07\x10"
+mtp_operation_get_object_info="\x08\x10"
+mtp_operation_get_object="\x09\x10"
+mtp_operation_get_thumb="\x0A\x10"
+mtp_operation_delete_object="\x0B\x10"
+mtp_operation_send_object_info="\x0C\x10"
+mtp_operation_send_object="\x0D\x10"
+mtp_operation_initiate_capture="\x0E\x10"
+mtp_operation_format_store="\x0F\x10"
+mtp_operation_reset_device="\x10\x10"
+mtp_operation_self_test="\x11\x10"
+mtp_operation_set_object_protection="\x12\x10"
+mtp_operation_power_down="\x13\x10"
+mtp_operation_get_device_prop_desc="\x14\x10"
+mtp_operation_get_device_prop_value="\x15\x10"
+mtp_operation_set_device_prop_value="\x16\x10"
+mtp_operation_reset_device_prop_value="\x17\x10"
+mtp_operation_terminate_open_capture="\x18\x10"
+mtp_operation_move_object="\x19\x10"
+mtp_operation_copy_object="\x1A\x10"
+mtp_operation_get_partial_object="\x1B\x10"
+mtp_operation_initiate_open_capture="\x1C\x10"
+mtp_operation_get_object_props_supported="\x01\x98"
+mtp_operation_get_object_prop_desc="\x02\x98"
+mtp_operation_get_object_prop_value="\x03\x98"
+mtp_operation_set_object_prop_value="\x04\x98"
+mtp_operation_get_object_prop_list="\x05\x98"
+mtp_operation_set_object_prop_list="\x06\x98"
+mtp_operation_get_interdependent_prop_desc="\x07\x98"
+mtp_operation_send_object_prop_list="\x08\x98"
+mtp_operation_get_object_references="\x10\x98"
+mtp_operation_set_object_references="\x11\x98"
+mtp_operation_skip="\x20\x98"
+mtp_operation_get_partial_object_64="\xC1\x95"
+mtp_operation_send_partial_object="\xC2\x95"
+mtp_operation_truncate_object="\xC3\x95"
+mtp_operation_begin_edit_object="\xC4\x95"
+mtp_operation_end_edit_object="\xC5\x95"
+
+# Association (for example, a folder)
+mtp_format_association="\x01\x30"
+
+# types
+mtp_type_undefined="\x00\x00"
+mtp_type_int8="\x01\x00"
+mtp_type_uint8="\x02\x00"
+mtp_type_int16="\x03\x00"
+mtp_type_uint16="\x04\x00"
+mtp_type_int32="\x05\x00"
+mtp_type_uint32="\x06\x00"
+mtp_type_int64="\x07\x00"
+mtp_type_uint64="\x08\x00"
+mtp_type_int128="\x09\x00"
+mtp_type_uint128="\x0A\x00"
+mtp_type_aint8="\x01\x40"
+mtp_type_auint8="\x02\x40"
+mtp_type_aint16="\x03\x40"
+mtp_type_auint16="\x04\x40"
+mtp_type_aint32="\x05\x40"
+mtp_type_auint32="\x06\x40"
+mtp_type_aint64="\x07\x40"
+mtp_type_auint64="\x08\x40"
+mtp_type_aint128="\x09\x40"
+mtp_type_auint128="\x0A\x40"
+mtp_type_str="\xFF\xFF"
+
+# also used for max size (>4GB)
+mtp_parent_root="\xFF\xFF\xFF\xFF"