Added fastboot_fuzzer

Test: ./fastboot_fuzzer
Bug: 189053436

Change-Id: Idf9be2f86238eb2c7090402adc54bbb9c0b43582
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 2c70778..339f392 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -408,3 +408,9 @@
         ":fastboot_test_vendor_boot_v4_with_frag"
     ],
 }
+
+cc_library_headers {
+    name: "fastboot_headers",
+    host_supported: true,
+    export_include_dirs: ["."],
+}
diff --git a/fastboot/fuzzer/Android.bp b/fastboot/fuzzer/Android.bp
new file mode 100644
index 0000000..fcd3bd6
--- /dev/null
+++ b/fastboot/fuzzer/Android.bp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 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.
+ *
+ */
+
+cc_fuzz {
+    name: "fastboot_fuzzer",
+    host_supported: true,
+    device_supported: false,
+    srcs: [
+        "fastboot_fuzzer.cpp",
+        "socket_mock_fuzz.cpp",
+    ],
+    header_libs: [
+        "bootimg_headers",
+        "fastboot_headers",
+    ],
+    static_libs: [
+        "libext4_utils",
+        "libcrypto",
+        "libfastboot",
+        "libbuildversion",
+        "libbase",
+        "libziparchive",
+        "libsparse",
+        "libutils",
+        "liblog",
+        "libz",
+        "libdiagnose_usb",
+        "libbase",
+        "libcutils",
+        "libgtest",
+        "libgtest_main",
+        "libbase",
+        "libadb_host",
+        "liblp",
+        "liblog",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 533764,
+    },
+}
diff --git a/fastboot/fuzzer/README.md b/fastboot/fuzzer/README.md
new file mode 100644
index 0000000..10b06ea
--- /dev/null
+++ b/fastboot/fuzzer/README.md
@@ -0,0 +1,51 @@
+# Fuzzer for libfastboot
+
+## Plugin Design Considerations
+The fuzzer plugin for libfastboot is designed based on the understanding of the
+source code and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libfastboot supports the following parameters:
+1. Year (parameter name: `year`)
+2. Month (parameter name: `month`)
+3. Day (parameter name: `day`)
+4. Version (parameter name: `version`)
+5. Fs Option (parameter name: `fsOption`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `year` | `2000` to `2127` | Value obtained from FuzzedDataProvider|
+| `month` | `1` to `12` | Value obtained from FuzzedDataProvider|
+| `day` | `1` to `31` | Value obtained from FuzzedDataProvider|
+| `version` | `0` to `127` | Value obtained from FuzzedDataProvider|
+| `fsOption` | 0. `casefold` 1. `projid` 2. `compress` | Value obtained from FuzzedDataProvider|
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build fastboot_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) fastboot_fuzzer_fuzzer
+```
+#### Steps to run
+To run on host
+```
+  $ $ANDROID_HOST_OUT/fuzz/${TARGET_ARCH}/fastboot_fuzzer/fastboot_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/fastboot/fuzzer/fastboot_fuzzer.cpp b/fastboot/fuzzer/fastboot_fuzzer.cpp
new file mode 100644
index 0000000..60940fe
--- /dev/null
+++ b/fastboot/fuzzer/fastboot_fuzzer.cpp
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2021 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/file.h>
+#include "fastboot.h"
+#include "socket.h"
+#include "socket_mock_fuzz.h"
+#include "tcp.h"
+#include "udp.h"
+#include "vendor_boot_img_utils.h"
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+using namespace std;
+
+const size_t kYearMin = 2000;
+const size_t kYearMax = 2127;
+const size_t kMonthMin = 1;
+const size_t kMonthMax = 12;
+const size_t kDayMin = 1;
+const size_t kDayMax = 31;
+const size_t kVersionMin = 0;
+const size_t kVersionMax = 127;
+const size_t kMaxStringSize = 100;
+const size_t kMinTimeout = 10;
+const size_t kMaxTimeout = 3000;
+const uint16_t kValidUdpPacketSize = 512;
+const uint16_t kMinUdpPackets = 1;
+const uint16_t kMaxUdpPackets = 10;
+
+const string kValidTcpHandshakeString = "FB01";
+const string kInvalidTcpHandshakeString = "FB00";
+const string kValidRamdiskName = "default";
+const string kVendorBootFile = "/tmp/vendorBootFile";
+const string kRamdiskFile = "/tmp/ramdiskFile";
+const char* kFsOptionsArray[] = {"casefold", "projid", "compress"};
+
+class FastbootFuzzer {
+  public:
+    void Process(const uint8_t* data, size_t size);
+
+  private:
+    void InvokeParseApi();
+    void InvokeSocket();
+    void InvokeTcp();
+    void InvokeUdp();
+    void InvokeVendorBootImgUtils(const uint8_t* data, size_t size);
+    bool MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,
+                              unique_ptr<Socket>* client, const string& hostname);
+    unique_ptr<FuzzedDataProvider> fdp_ = nullptr;
+};
+
+void FastbootFuzzer::InvokeParseApi() {
+    boot_img_hdr_v1 hdr = {};
+    FastBootTool fastBoot;
+
+    int32_t year = fdp_->ConsumeIntegralInRange<int32_t>(kYearMin, kYearMax);
+    int32_t month = fdp_->ConsumeIntegralInRange<int32_t>(kMonthMin, kMonthMax);
+    int32_t day = fdp_->ConsumeIntegralInRange<int32_t>(kDayMin, kDayMax);
+    string date = to_string(year) + "-" + to_string(month) + "-" + to_string(day);
+    fastBoot.ParseOsPatchLevel(&hdr, date.c_str());
+
+    int32_t major = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
+    int32_t minor = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
+    int32_t patch = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);
+    string version = to_string(major) + "." + to_string(minor) + "." + to_string(patch);
+    fastBoot.ParseOsVersion(&hdr, version.c_str());
+
+    fastBoot.ParseFsOption(fdp_->PickValueInArray(kFsOptionsArray));
+}
+
+bool FastbootFuzzer::MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,
+                                          unique_ptr<Socket>* client,
+                                          const string& hostname = "localhost") {
+    *server = Socket::NewServer(protocol, 0);
+    if (*server == nullptr) {
+        return false;
+    }
+    *client = Socket::NewClient(protocol, hostname, (*server)->GetLocalPort(), nullptr);
+    if (*client == nullptr) {
+        return false;
+    }
+    if (protocol == Socket::Protocol::kTcp) {
+        *server = (*server)->Accept();
+        if (*server == nullptr) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void FastbootFuzzer::InvokeSocket() {
+    unique_ptr<Socket> server, client;
+
+    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+        if (MakeConnectedSockets(protocol, &server, &client)) {
+            string message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+            client->Send(message.c_str(), message.length());
+            string received(message.length(), '\0');
+            if (fdp_->ConsumeBool()) {
+                client->Close();
+            }
+            if (fdp_->ConsumeBool()) {
+                server->Close();
+            }
+            server->ReceiveAll(&received[0], received.length(),
+                               /* timeout_ms */
+                               fdp_->ConsumeIntegralInRange<size_t>(kMinTimeout, kMaxTimeout));
+            server->Close();
+            client->Close();
+        }
+    }
+}
+
+void FastbootFuzzer::InvokeTcp() {
+    /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */
+    SocketMockFuzz* tcp_mock = new SocketMockFuzz;
+    tcp_mock->ExpectSend(fdp_->ConsumeBool() ? kValidTcpHandshakeString
+                                             : kInvalidTcpHandshakeString);
+    tcp_mock->AddReceive(fdp_->ConsumeBool() ? kValidTcpHandshakeString
+                                             : kInvalidTcpHandshakeString);
+
+    string error;
+    unique_ptr<Transport> transport = tcp::internal::Connect(unique_ptr<Socket>(tcp_mock), &error);
+
+    if (transport.get()) {
+        string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+        if (fdp_->ConsumeBool()) {
+            tcp_mock->ExpectSend(write_message);
+        } else {
+            tcp_mock->ExpectSendFailure(write_message);
+        }
+        string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+        if (fdp_->ConsumeBool()) {
+            tcp_mock->AddReceive(read_message);
+        } else {
+            tcp_mock->AddReceiveFailure();
+        }
+
+        transport->Write(write_message.data(), write_message.length());
+
+        string buffer(read_message.length(), '\0');
+        transport->Read(&buffer[0], buffer.length());
+
+        transport->Close();
+    }
+}
+
+static string PacketValue(uint16_t value) {
+    return string{static_cast<char>(value >> 8), static_cast<char>(value)};
+}
+
+static string ErrorPacket(uint16_t sequence, const string& message = "",
+                          char flags = udp::internal::kFlagNone) {
+    return string{udp::internal::kIdError, flags} + PacketValue(sequence) + message;
+}
+
+static string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {
+    return string{udp::internal::kIdInitialization, udp::internal::kFlagNone} +
+           PacketValue(sequence) + PacketValue(version) + PacketValue(max_packet_size);
+}
+
+static string QueryPacket(uint16_t sequence, uint16_t new_sequence) {
+    return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence) +
+           PacketValue(new_sequence);
+}
+
+static string QueryPacket(uint16_t sequence) {
+    return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence);
+}
+
+static string FastbootPacket(uint16_t sequence, const string& data = "",
+                             char flags = udp::internal::kFlagNone) {
+    return string{udp::internal::kIdFastboot, flags} + PacketValue(sequence) + data;
+}
+
+void FastbootFuzzer::InvokeUdp() {
+    /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */
+    SocketMockFuzz* udp_mock = new SocketMockFuzz;
+    uint16_t starting_sequence = fdp_->ConsumeIntegral<uint16_t>();
+    int32_t device_max_packet_size = fdp_->ConsumeBool() ? kValidUdpPacketSize
+                                                         : fdp_->ConsumeIntegralInRange<uint16_t>(
+                                                                   0, kValidUdpPacketSize - 1);
+    udp_mock->ExpectSend(QueryPacket(0));
+    udp_mock->AddReceive(QueryPacket(0, starting_sequence));
+    udp_mock->ExpectSend(InitPacket(starting_sequence, udp::internal::kProtocolVersion,
+                                    udp::internal::kHostMaxPacketSize));
+    udp_mock->AddReceive(
+            InitPacket(starting_sequence, udp::internal::kProtocolVersion, device_max_packet_size));
+
+    string error;
+    unique_ptr<Transport> transport = udp::internal::Connect(unique_ptr<Socket>(udp_mock), &error);
+    bool is_transport_initialized = transport != nullptr && error.empty();
+
+    if (is_transport_initialized) {
+        uint16_t num_packets =
+                fdp_->ConsumeIntegralInRange<uint16_t>(kMinUdpPackets, kMaxUdpPackets);
+
+        for (uint16_t i = 0; i < num_packets; ++i) {
+            string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+            string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);
+            if (fdp_->ConsumeBool()) {
+                udp_mock->ExpectSend(FastbootPacket(i, write_message));
+            } else {
+                udp_mock->ExpectSend(ErrorPacket(i, write_message));
+            }
+
+            if (fdp_->ConsumeBool()) {
+                udp_mock->AddReceive(FastbootPacket(i, read_message));
+            } else {
+                udp_mock->AddReceive(ErrorPacket(i, read_message));
+            }
+            transport->Write(write_message.data(), write_message.length());
+            string buffer(read_message.length(), '\0');
+            transport->Read(&buffer[0], buffer.length());
+        }
+        transport->Close();
+    }
+}
+
+void FastbootFuzzer::InvokeVendorBootImgUtils(const uint8_t* data, size_t size) {
+    int32_t vendor_boot_fd = open(kVendorBootFile.c_str(), O_CREAT | O_RDWR, 0644);
+    if (vendor_boot_fd < 0) {
+        return;
+    }
+    int32_t ramdisk_fd = open(kRamdiskFile.c_str(), O_CREAT | O_RDWR, 0644);
+    if (ramdisk_fd < 0) {
+        return;
+    }
+    write(vendor_boot_fd, data, size);
+    write(ramdisk_fd, data, size);
+    string ramdisk_name = fdp_->ConsumeBool() ? kValidRamdiskName
+                                              : fdp_->ConsumeRandomLengthString(kMaxStringSize);
+    string content_vendor_boot_fd = {};
+    string content_ramdisk_fd = {};
+    lseek(vendor_boot_fd, 0, SEEK_SET);
+    lseek(ramdisk_fd, 0, SEEK_SET);
+    android::base::ReadFdToString(vendor_boot_fd, &content_vendor_boot_fd);
+    android::base::ReadFdToString(ramdisk_fd, &content_ramdisk_fd);
+    uint64_t vendor_boot_size =
+            fdp_->ConsumeBool() ? content_vendor_boot_fd.size() : fdp_->ConsumeIntegral<uint64_t>();
+    uint64_t ramdisk_size =
+            fdp_->ConsumeBool() ? content_ramdisk_fd.size() : fdp_->ConsumeIntegral<uint64_t>();
+    (void)replace_vendor_ramdisk(vendor_boot_fd, vendor_boot_size, ramdisk_name, ramdisk_fd,
+                                 ramdisk_size);
+    close(vendor_boot_fd);
+    close(ramdisk_fd);
+}
+
+void FastbootFuzzer::Process(const uint8_t* data, size_t size) {
+    fdp_ = make_unique<FuzzedDataProvider>(data, size);
+    InvokeParseApi();
+    InvokeSocket();
+    InvokeTcp();
+    InvokeUdp();
+    InvokeVendorBootImgUtils(data, size);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    FastbootFuzzer fastbootFuzzer;
+    fastbootFuzzer.Process(data, size);
+    return 0;
+}
diff --git a/fastboot/fuzzer/socket_mock_fuzz.cpp b/fastboot/fuzzer/socket_mock_fuzz.cpp
new file mode 100644
index 0000000..df96eb0
--- /dev/null
+++ b/fastboot/fuzzer/socket_mock_fuzz.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2021 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 "socket_mock_fuzz.h"
+
+SocketMockFuzz::SocketMockFuzz() : Socket(INVALID_SOCKET) {}
+
+SocketMockFuzz::~SocketMockFuzz() {}
+
+bool SocketMockFuzz::Send(const void* data, size_t length) {
+    if (events_.empty()) {
+        return false;
+    }
+
+    if (events_.front().type != EventType::kSend) {
+        return false;
+    }
+
+    std::string message(reinterpret_cast<const char*>(data), length);
+    if (events_.front().message != message) {
+        return false;
+    }
+
+    bool return_value = events_.front().status;
+    events_.pop();
+    return return_value;
+}
+
+// Mock out multi-buffer send to be one large send, since that's what it should looks like from
+// the user's perspective.
+bool SocketMockFuzz::Send(std::vector<cutils_socket_buffer_t> buffers) {
+    std::string data;
+    for (const auto& buffer : buffers) {
+        data.append(reinterpret_cast<const char*>(buffer.data), buffer.length);
+    }
+    return Send(data.data(), data.size());
+}
+
+ssize_t SocketMockFuzz::Receive(void* data, size_t length, int /*timeout_ms*/) {
+    if (events_.empty()) {
+        return -1;
+    }
+
+    const Event& event = events_.front();
+    if (event.type != EventType::kReceive) {
+        return -1;
+    }
+
+    const std::string& message = event.message;
+    if (message.length() > length) {
+        return -1;
+    }
+
+    receive_timed_out_ = event.status;
+    ssize_t return_value = message.length();
+
+    // Empty message indicates failure.
+    if (message.empty()) {
+        return_value = -1;
+    } else {
+        memcpy(data, message.data(), message.length());
+    }
+
+    events_.pop();
+    return return_value;
+}
+
+int SocketMockFuzz::Close() {
+    return 0;
+}
+
+std::unique_ptr<Socket> SocketMockFuzz::Accept() {
+    if (events_.empty()) {
+        return nullptr;
+    }
+
+    if (events_.front().type != EventType::kAccept) {
+        return nullptr;
+    }
+
+    std::unique_ptr<Socket> sock = std::move(events_.front().sock);
+    events_.pop();
+    return sock;
+}
+
+void SocketMockFuzz::ExpectSend(std::string message) {
+    events_.push(Event(EventType::kSend, std::move(message), true, nullptr));
+}
+
+void SocketMockFuzz::ExpectSendFailure(std::string message) {
+    events_.push(Event(EventType::kSend, std::move(message), false, nullptr));
+}
+
+void SocketMockFuzz::AddReceive(std::string message) {
+    events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));
+}
+
+void SocketMockFuzz::AddReceiveTimeout() {
+    events_.push(Event(EventType::kReceive, "", true, nullptr));
+}
+
+void SocketMockFuzz::AddReceiveFailure() {
+    events_.push(Event(EventType::kReceive, "", false, nullptr));
+}
+
+void SocketMockFuzz::AddAccept(std::unique_ptr<Socket> sock) {
+    events_.push(Event(EventType::kAccept, "", false, std::move(sock)));
+}
+
+SocketMockFuzz::Event::Event(EventType _type, std::string _message, ssize_t _status,
+                             std::unique_ptr<Socket> _sock)
+    : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}
diff --git a/fastboot/fuzzer/socket_mock_fuzz.h b/fastboot/fuzzer/socket_mock_fuzz.h
new file mode 100644
index 0000000..67bd0d6
--- /dev/null
+++ b/fastboot/fuzzer/socket_mock_fuzz.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2021 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.
+ *
+ */
+
+#pragma once
+
+#include <memory>
+#include <queue>
+#include <string>
+
+#include <android-base/macros.h>
+
+#include "socket.h"
+
+class SocketMockFuzz : public Socket {
+  public:
+    SocketMockFuzz();
+    ~SocketMockFuzz() override;
+
+    bool Send(const void* data, size_t length) override;
+    bool Send(std::vector<cutils_socket_buffer_t> buffers) override;
+    ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+    int Close() override;
+    virtual std::unique_ptr<Socket> Accept();
+
+    // Adds an expectation for Send().
+    void ExpectSend(std::string message);
+
+    // Adds an expectation for Send() that returns false.
+    void ExpectSendFailure(std::string message);
+
+    // Adds data to provide for Receive().
+    void AddReceive(std::string message);
+
+    // Adds a Receive() timeout after which ReceiveTimedOut() will return true.
+    void AddReceiveTimeout();
+
+    // Adds a Receive() failure after which ReceiveTimedOut() will return false.
+    void AddReceiveFailure();
+
+    // Adds a Socket to return from Accept().
+    void AddAccept(std::unique_ptr<Socket> sock);
+
+  private:
+    enum class EventType { kSend, kReceive, kAccept };
+
+    struct Event {
+        Event(EventType _type, std::string _message, ssize_t _status,
+              std::unique_ptr<Socket> _sock);
+
+        EventType type;
+        std::string message;
+        bool status;  // Return value for Send() or timeout status for Receive().
+        std::unique_ptr<Socket> sock;
+    };
+
+    std::queue<Event> events_;
+
+    DISALLOW_COPY_AND_ASSIGN(SocketMockFuzz);
+};