/* * 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 #include "fastboot.h" #include "socket.h" #include "socket_mock_fuzz.h" #include "tcp.h" #include "udp.h" #include "vendor_boot_img_utils.h" #include 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* server, unique_ptr* client, const string& hostname); unique_ptr fdp_ = nullptr; }; void FastbootFuzzer::InvokeParseApi() { boot_img_hdr_v1 hdr = {}; FastBootTool fastBoot; int32_t year = fdp_->ConsumeIntegralInRange(kYearMin, kYearMax); int32_t month = fdp_->ConsumeIntegralInRange(kMonthMin, kMonthMax); int32_t day = fdp_->ConsumeIntegralInRange(kDayMin, kDayMax); string date = to_string(year) + "-" + to_string(month) + "-" + to_string(day); fastBoot.ParseOsPatchLevel(&hdr, date.c_str()); int32_t major = fdp_->ConsumeIntegralInRange(kVersionMin, kVersionMax); int32_t minor = fdp_->ConsumeIntegralInRange(kVersionMin, kVersionMax); int32_t patch = fdp_->ConsumeIntegralInRange(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* server, unique_ptr* 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 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(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 = tcp::internal::Connect(unique_ptr(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(value >> 8), static_cast(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(); int32_t device_max_packet_size = fdp_->ConsumeBool() ? kValidUdpPacketSize : fdp_->ConsumeIntegralInRange( 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 = udp::internal::Connect(unique_ptr(udp_mock), &error); bool is_transport_initialized = transport != nullptr && error.empty(); if (is_transport_initialized) { uint16_t num_packets = fdp_->ConsumeIntegralInRange(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 ramdisk_size = fdp_->ConsumeBool() ? content_ramdisk_fd.size() : fdp_->ConsumeIntegral(); (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(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; }