Merge "Addressing error-code propagation for remount service."
diff --git a/debuggerd/libdebuggerd/test/UnwinderMock.h b/debuggerd/libdebuggerd/test/UnwinderMock.h
index 8f84346..1e3c559 100644
--- a/debuggerd/libdebuggerd/test/UnwinderMock.h
+++ b/debuggerd/libdebuggerd/test/UnwinderMock.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <memory>
+
 #include <unwindstack/MapInfo.h>
 #include <unwindstack/Maps.h>
 #include <unwindstack/Unwinder.h>
@@ -31,7 +33,7 @@
   }
 
   void MockSetBuildID(uint64_t offset, const std::string& build_id) {
-    unwindstack::MapInfo* map_info = GetMaps()->Find(offset);
+    std::shared_ptr<unwindstack::MapInfo> map_info = GetMaps()->Find(offset);
     if (map_info != nullptr) {
       map_info->SetBuildID(std::string(build_id));
     }
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 534d7be..1835f0e 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -107,7 +107,7 @@
     // In this case, the sp will be in either an invalid map if triggered
     // on the main thread, or in a guard map if in another thread, which
     // will be the first case or second case from below.
-    unwindstack::MapInfo* map_info = maps->Find(sp);
+    auto map_info = maps->Find(sp);
     if (map_info == nullptr) {
       return "stack pointer is in a non-existent map; likely due to stack overflow.";
     } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
@@ -158,7 +158,7 @@
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
     uint64_t fault_addr = reinterpret_cast<uint64_t>(si->si_addr);
-    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+    auto map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
     } else {
@@ -396,7 +396,7 @@
   regs->IterateRegisters([log, maps, memory](const char* reg_name, uint64_t reg_value) {
     std::string label{"memory near "s + reg_name};
     if (maps) {
-      unwindstack::MapInfo* map_info = maps->Find(untag_address(reg_value));
+      auto map_info = maps->Find(untag_address(reg_value));
       if (map_info != nullptr && !map_info->name().empty()) {
         label += " (" + map_info->name() + ")";
       }
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 6c380a1..b1c4ef3 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -103,7 +103,7 @@
     // In this case, the sp will be in either an invalid map if triggered
     // on the main thread, or in a guard map if in another thread, which
     // will be the first case or second case from below.
-    unwindstack::MapInfo* map_info = maps->Find(sp);
+    std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(sp);
     if (map_info == nullptr) {
       return "stack pointer is in a non-existent map; likely due to stack overflow.";
     } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {
@@ -226,7 +226,7 @@
       cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps);
     }
   } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {
-    unwindstack::MapInfo* map_info = maps->Find(fault_addr);
+    auto map_info = maps->Find(fault_addr);
     if (map_info != nullptr && map_info->flags() == PROT_EXEC) {
       cause = "execute-only (no-read) memory access error; likely due to data in .text.";
     } else {
@@ -342,8 +342,8 @@
 
   f->set_file_map_offset(frame.map_elf_start_offset);
 
-  unwindstack::MapInfo* map_info = maps->Find(frame.map_start);
-  if (map_info) {
+  auto map_info = maps->Find(frame.map_start);
+  if (map_info.get() != nullptr) {
     f->set_build_id(map_info->GetPrintableBuildID());
   }
 }
@@ -370,7 +370,7 @@
           MemoryDump dump;
 
           dump.set_register_name(name);
-          unwindstack::MapInfo* map_info = maps->Find(untag_address(value));
+          std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));
           if (map_info) {
             dump.set_mapping_name(map_info->name());
           }
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/Android.mk b/fastboot/Android.mk
index 0e918a3..322fe5c 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -18,10 +18,10 @@
 # Package fastboot-related executables.
 #
 
-my_dist_files := $(SOONG_HOST_OUT_EXECUTABLES)/mke2fs
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/e2fsdroid
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/make_f2fs_casefold
-my_dist_files += $(SOONG_HOST_OUT_EXECUTABLES)/sload_f2fs
+my_dist_files := $(HOST_OUT_EXECUTABLES)/mke2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/e2fsdroid
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs
+my_dist_files += $(HOST_OUT_EXECUTABLES)/make_f2fs_casefold
+my_dist_files += $(HOST_OUT_EXECUTABLES)/sload_f2fs
 $(call dist-for-goals,dist_files sdk win_sdk,$(my_dist_files))
 my_dist_files :=
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);
+};
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 9b6c3dd..609bd11 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -130,10 +130,12 @@
             if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {
                 const auto arg = flag.substr(equal_sign + 1);
                 if (entry->fs_type == "f2fs" && StartsWith(flag, "reserve_root=")) {
-                    if (!ParseInt(arg, &entry->reserved_size)) {
+                    off64_t size_in_4k_blocks;
+                    if (!ParseInt(arg, &size_in_4k_blocks, static_cast<off64_t>(0),
+                                  std::numeric_limits<off64_t>::max() >> 12)) {
                         LWARNING << "Warning: reserve_root= flag malformed: " << arg;
                     } else {
-                        entry->reserved_size <<= 12;
+                        entry->reserved_size = size_in_4k_blocks << 12;
                     }
                 } else if (StartsWith(flag, "lowerdir=")) {
                     entry->lowerdir = std::move(arg);
@@ -442,7 +444,81 @@
     return "";
 }
 
-bool ReadFstabFile(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
+/* Extracts <device>s from the by-name symlinks specified in a fstab:
+ *   /dev/block/<type>/<device>/by-name/<partition>
+ *
+ * <type> can be: platform, pci or vbd.
+ *
+ * For example, given the following entries in the input fstab:
+ *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
+ *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
+ * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
+ */
+std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
+    std::set<std::string> boot_devices;
+
+    for (const auto& entry : fstab) {
+        std::string blk_device = entry.blk_device;
+        // Skips blk_device that doesn't conform to the format.
+        if (!android::base::StartsWith(blk_device, "/dev/block") ||
+            android::base::StartsWith(blk_device, "/dev/block/by-name") ||
+            android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
+            continue;
+        }
+        // Skips non-by_name blk_device.
+        // /dev/block/<type>/<device>/by-name/<partition>
+        //                           ^ slash_by_name
+        auto slash_by_name = blk_device.find("/by-name");
+        if (slash_by_name == std::string::npos) continue;
+        blk_device.erase(slash_by_name);  // erases /by-name/<partition>
+
+        // Erases /dev/block/, now we have <type>/<device>
+        blk_device.erase(0, std::string("/dev/block/").size());
+
+        // <type>/<device>
+        //       ^ first_slash
+        auto first_slash = blk_device.find('/');
+        if (first_slash == std::string::npos) continue;
+
+        auto boot_device = blk_device.substr(first_slash + 1);
+        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
+    }
+
+    return boot_devices;
+}
+
+FstabEntry BuildDsuUserdataFstabEntry() {
+    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
+
+    FstabEntry userdata = {
+            .blk_device = "userdata_gsi",
+            .mount_point = "/data",
+            .fs_type = "ext4",
+            .flags = kFlags,
+            .reserved_size = 128 * 1024 * 1024,
+    };
+    userdata.fs_mgr_flags.wait = true;
+    userdata.fs_mgr_flags.check = true;
+    userdata.fs_mgr_flags.logical = true;
+    userdata.fs_mgr_flags.quota = true;
+    userdata.fs_mgr_flags.late_mount = true;
+    userdata.fs_mgr_flags.formattable = true;
+    return userdata;
+}
+
+bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
+    auto iter = std::remove_if(fstab->begin(), fstab->end(),
+                               [&](const auto& entry) { return entry.mount_point == mount_point; });
+    if (iter != fstab->end()) {
+        fstab->erase(iter, fstab->end());
+        return true;
+    }
+    return false;
+}
+
+}  // namespace
+
+bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out) {
     ssize_t len;
     size_t alloc_len = 0;
     char *line = NULL;
@@ -528,80 +604,6 @@
     return false;
 }
 
-/* Extracts <device>s from the by-name symlinks specified in a fstab:
- *   /dev/block/<type>/<device>/by-name/<partition>
- *
- * <type> can be: platform, pci or vbd.
- *
- * For example, given the following entries in the input fstab:
- *   /dev/block/platform/soc/1da4000.ufshc/by-name/system
- *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor
- * it returns a set { "soc/1da4000.ufshc", "soc.0/f9824900.sdhci" }.
- */
-std::set<std::string> ExtraBootDevices(const Fstab& fstab) {
-    std::set<std::string> boot_devices;
-
-    for (const auto& entry : fstab) {
-        std::string blk_device = entry.blk_device;
-        // Skips blk_device that doesn't conform to the format.
-        if (!android::base::StartsWith(blk_device, "/dev/block") ||
-            android::base::StartsWith(blk_device, "/dev/block/by-name") ||
-            android::base::StartsWith(blk_device, "/dev/block/bootdevice/by-name")) {
-            continue;
-        }
-        // Skips non-by_name blk_device.
-        // /dev/block/<type>/<device>/by-name/<partition>
-        //                           ^ slash_by_name
-        auto slash_by_name = blk_device.find("/by-name");
-        if (slash_by_name == std::string::npos) continue;
-        blk_device.erase(slash_by_name);  // erases /by-name/<partition>
-
-        // Erases /dev/block/, now we have <type>/<device>
-        blk_device.erase(0, std::string("/dev/block/").size());
-
-        // <type>/<device>
-        //       ^ first_slash
-        auto first_slash = blk_device.find('/');
-        if (first_slash == std::string::npos) continue;
-
-        auto boot_device = blk_device.substr(first_slash + 1);
-        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));
-    }
-
-    return boot_devices;
-}
-
-FstabEntry BuildDsuUserdataFstabEntry() {
-    constexpr uint32_t kFlags = MS_NOATIME | MS_NOSUID | MS_NODEV;
-
-    FstabEntry userdata = {
-            .blk_device = "userdata_gsi",
-            .mount_point = "/data",
-            .fs_type = "ext4",
-            .flags = kFlags,
-            .reserved_size = 128 * 1024 * 1024,
-    };
-    userdata.fs_mgr_flags.wait = true;
-    userdata.fs_mgr_flags.check = true;
-    userdata.fs_mgr_flags.logical = true;
-    userdata.fs_mgr_flags.quota = true;
-    userdata.fs_mgr_flags.late_mount = true;
-    userdata.fs_mgr_flags.formattable = true;
-    return userdata;
-}
-
-bool EraseFstabEntry(Fstab* fstab, const std::string& mount_point) {
-    auto iter = std::remove_if(fstab->begin(), fstab->end(),
-                               [&](const auto& entry) { return entry.mount_point == mount_point; });
-    if (iter != fstab->end()) {
-        fstab->erase(iter, fstab->end());
-        return true;
-    }
-    return false;
-}
-
-}  // namespace
-
 void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
                           const std::vector<std::string>& dsu_partitions) {
     static constexpr char kDsuKeysDir[] = "/avb";
@@ -707,7 +709,7 @@
     bool is_proc_mounts = path == "/proc/mounts";
 
     Fstab fstab;
-    if (!ReadFstabFile(fstab_file.get(), is_proc_mounts, &fstab)) {
+    if (!ReadFstabFromFp(fstab_file.get(), is_proc_mounts, &fstab)) {
         LERROR << __FUNCTION__ << "(): failed to load fstab from : '" << path << "'";
         return false;
     }
@@ -762,7 +764,7 @@
         return false;
     }
 
-    if (!ReadFstabFile(fstab_file.get(), false, fstab)) {
+    if (!ReadFstabFromFp(fstab_file.get(), false, fstab)) {
         if (verbose) {
             LERROR << __FUNCTION__ << "(): failed to load fstab from kernel:" << std::endl
                    << fstab_buf;
diff --git a/fs_mgr/fuzz/Android.bp b/fs_mgr/fuzz/Android.bp
new file mode 100644
index 0000000..f0afd28
--- /dev/null
+++ b/fs_mgr/fuzz/Android.bp
@@ -0,0 +1,35 @@
+//
+// 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: "libfstab_fuzzer",
+  srcs: [
+    "fs_mgr_fstab_fuzzer.cpp",
+  ],
+  static_libs: [
+    "libfstab",
+  ],
+  shared_libs: [
+    "libbase",
+  ],
+
+  dictionary: "fstab.dict",
+  fuzz_config: {
+    cc: [
+      "yochiang@google.com",
+    ],
+  },
+}
diff --git a/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
new file mode 100644
index 0000000..1fddbf8
--- /dev/null
+++ b/fs_mgr/fuzz/fs_mgr_fstab_fuzzer.cpp
@@ -0,0 +1,30 @@
+//
+// 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 <cstdio>
+
+#include <fstab/fstab.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    std::unique_ptr<FILE, decltype(&fclose)> fstab_file(
+            fmemopen(static_cast<void*>(const_cast<uint8_t*>(data)), size, "r"), fclose);
+    if (fstab_file == nullptr) {
+        return 0;
+    }
+    android::fs_mgr::Fstab fstab;
+    android::fs_mgr::ReadFstabFromFp(fstab_file.get(), /* proc_mounts= */ false, &fstab);
+    return 0;
+}
diff --git a/fs_mgr/fuzz/fstab.dict b/fs_mgr/fuzz/fstab.dict
new file mode 100644
index 0000000..84dddf7
--- /dev/null
+++ b/fs_mgr/fuzz/fstab.dict
@@ -0,0 +1,70 @@
+"#"
+"="
+","
+"f2fs"
+
+# mount flags
+"noatime"
+"noexec"
+"nosuid"
+"nodev"
+"nodiratime"
+"ro"
+"rw"
+"sync"
+"remount"
+"bind"
+"rec"
+"unbindable"
+"private"
+"slave"
+"shared"
+"defaults"
+
+# fs_mgr flags
+"wait"
+"check"
+"nonremovable"
+"recoveryonly"
+"noemulatedsd"
+"notrim"
+"verify"
+"formattable"
+"slotselect"
+"latemount"
+"nofail"
+"verifyatboot"
+"quota"
+"avb"
+"logical"
+"checkpoint=block"
+"checkpoint=fs"
+"first_stage_mount"
+"slotselect_other"
+"fsverity"
+"metadata_csum"
+"fscompress"
+"overlayfs_remove_missing_lowerdir"
+
+# fs_mgr flags that expect an argument
+"reserve_root="
+"lowerdir="
+"encryptable="
+"voldmanaged="
+"length="
+"swapprio="
+"zramsize="
+"forceencrypt="
+"fileencryption="
+"forcefdeorfbe="
+"max_comp_streams="
+"reservedsize="
+"readahead_size_kb="
+"eraseblk="
+"logicalblk="
+"avb_keys="
+"avb="
+"keydirectory="
+"metadata_encryption="
+"sysfs_path="
+"zram_backingdev_size="
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 9a4ed46..d0f32a3 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -99,6 +99,9 @@
 // Unless explicitly requested, a lookup on mount point should always return the 1st one.
 using Fstab = std::vector<FstabEntry>;
 
+// Exported for testability. Regular users should use ReadFstabFromfile().
+bool ReadFstabFromFp(FILE* fstab_file, bool proc_mounts, Fstab* fstab_out);
+
 bool ReadFstabFromFile(const std::string& path, Fstab* fstab);
 bool ReadFstabFromDt(Fstab* fstab, bool verbose = true);
 bool ReadDefaultFstab(Fstab* fstab);
diff --git a/fs_mgr/libfiemap/fiemap_writer.cpp b/fs_mgr/libfiemap/fiemap_writer.cpp
index 8acb885..71dadd0 100644
--- a/fs_mgr/libfiemap/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap/fiemap_writer.cpp
@@ -498,24 +498,6 @@
     return IsFilePinned(fd, file_path, sfs.f_type);
 }
 
-static bool CountFiemapExtents(int file_fd, const std::string& file_path, uint32_t* num_extents) {
-    struct fiemap fiemap = {};
-    fiemap.fm_start = 0;
-    fiemap.fm_length = UINT64_MAX;
-    fiemap.fm_flags = FIEMAP_FLAG_SYNC;
-    fiemap.fm_extent_count = 0;
-
-    if (ioctl(file_fd, FS_IOC_FIEMAP, &fiemap)) {
-        PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
-        return false;
-    }
-
-    if (num_extents) {
-        *num_extents = fiemap.fm_mapped_extents;
-    }
-    return true;
-}
-
 static bool IsValidExtent(const fiemap_extent* extent, std::string_view file_path) {
     if (extent->fe_flags & kUnsupportedExtentFlags) {
         LOG(ERROR) << "Extent at location " << extent->fe_logical << " of file " << file_path
@@ -530,12 +512,12 @@
 }
 
 static bool FiemapToExtents(struct fiemap* fiemap, std::vector<struct fiemap_extent>* extents,
-                            uint32_t num_extents, std::string_view file_path) {
-    if (num_extents == 0) return false;
-
+                            std::string_view file_path) {
+    uint32_t num_extents = fiemap->fm_mapped_extents;
     const struct fiemap_extent* last_extent = &fiemap->fm_extents[num_extents - 1];
     if (!IsLastExtent(last_extent)) {
-        LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path;
+        LOG(ERROR) << "FIEMAP did not return a final extent for file: " << file_path
+                   << " num_extents=" << num_extents << " max_extents=" << kMaxExtents;
         return false;
     }
 
@@ -577,21 +559,7 @@
 
 static bool ReadFiemap(int file_fd, const std::string& file_path,
                        std::vector<struct fiemap_extent>* extents) {
-    uint32_t num_extents;
-    if (!CountFiemapExtents(file_fd, file_path, &num_extents)) {
-        return false;
-    }
-    if (num_extents == 0) {
-        LOG(ERROR) << "File " << file_path << " has zero extents";
-        return false;
-    }
-    if (num_extents > kMaxExtents) {
-        LOG(ERROR) << "File has " << num_extents << ", maximum is " << kMaxExtents << ": "
-                   << file_path;
-        return false;
-    }
-
-    uint64_t fiemap_size = sizeof(struct fiemap) + num_extents * sizeof(struct fiemap_extent);
+    uint64_t fiemap_size = sizeof(struct fiemap) + kMaxExtents * sizeof(struct fiemap_extent);
     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
     if (buffer == nullptr) {
         LOG(ERROR) << "Failed to allocate memory for fiemap";
@@ -603,19 +571,13 @@
     fiemap->fm_length = UINT64_MAX;
     // make sure file is synced to disk before we read the fiemap
     fiemap->fm_flags = FIEMAP_FLAG_SYNC;
-    fiemap->fm_extent_count = num_extents;
+    fiemap->fm_extent_count = kMaxExtents;
 
     if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
         PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
         return false;
     }
-    if (fiemap->fm_mapped_extents != num_extents) {
-        LOG(ERROR) << "FIEMAP returned unexpected extent count (" << num_extents
-                   << " expected, got " << fiemap->fm_mapped_extents << ") for file: " << file_path;
-        return false;
-    }
-
-    return FiemapToExtents(fiemap, extents, num_extents, file_path);
+    return FiemapToExtents(fiemap, extents, file_path);
 }
 
 static bool ReadFibmap(int file_fd, const std::string& file_path,
diff --git a/fs_mgr/libsnapshot/OWNERS b/fs_mgr/libsnapshot/OWNERS
index 801c446..37319fe 100644
--- a/fs_mgr/libsnapshot/OWNERS
+++ b/fs_mgr/libsnapshot/OWNERS
@@ -1,3 +1,4 @@
+# Bug component: 30545
 balsini@google.com
 dvander@google.com
 elsk@google.com
diff --git a/init/README.md b/init/README.md
index 64c6b1c..6c29b07 100644
--- a/init/README.md
+++ b/init/README.md
@@ -733,7 +733,10 @@
 `verity_update_state`
 > Internal implementation detail used to update dm-verity state and
   set the partition._mount-point_.verified properties used by adb remount
-  because fs\_mgr can't set them directly itself.
+  because fs\_mgr can't set them directly itself. This is required since
+  Android 12, because CtsNativeVerifiedBootTestCases will read property
+  "partition.${partition}.verified.hash_alg" to check that sha1 is not used.
+  See https://r.android.com/1546980 for more details.
 
 `wait <path> [ <timeout> ]`
 > Poll for the existence of the given file and return when found,
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index ac6b7b2..5f34cc4 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -99,7 +99,7 @@
 
     std::string vbmeta_digest = GetProperty("ro.boot.vbmeta.digest", "");
     ASSERT_GE(vbmeta_digest.size(), 8u);
-    std::string build_id = GetProperty("ro.boot.build.id", "");
+    std::string build_id = GetProperty("ro.build.id", "");
     // Check that the build id is constructed with the prefix of vbmeta digest
     std::string expected_build_id = legacy_build_id + "." + vbmeta_digest.substr(0, 8);
     ASSERT_EQ(expected_build_id, build_id);
diff --git a/libcutils/include/private/android_filesystem_config.h b/libcutils/include/private/android_filesystem_config.h
index c471fa0..e65fe92 100644
--- a/libcutils/include/private/android_filesystem_config.h
+++ b/libcutils/include/private/android_filesystem_config.h
@@ -130,6 +130,7 @@
 #define AID_VIRTUALIZATIONSERVICE 1081 /* VirtualizationService daemon */
 #define AID_ARTD 1082             /* ART Service daemon */
 #define AID_UWB 1083              /* UWB subsystem */
+#define AID_THREAD_NETWORK 1084   /* Thread Network subsystem */
 /* Changes to this file must be made in AOSP, *not* in internal branches. */
 
 #define AID_SHELL 2000 /* adb and debug shell user */
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 449a505..45d3c7c 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -183,7 +183,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "cpu",
+            "Path": "dex2oat"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
@@ -638,7 +650,7 @@
     },
     {
       "Name": "Dex2OatBootComplete",
-      "Profiles": [ "SCHED_SP_BACKGROUND" ]
+      "Profiles": [ "Dex2oatPerformance", "LowIoPriority", "TimerSlackHigh" ]
     }
   ]
 }
diff --git a/libprocessgroup/profiles/task_profiles_28.json b/libprocessgroup/profiles/task_profiles_28.json
index 56053e0..e7be548 100644
--- a/libprocessgroup/profiles/task_profiles_28.json
+++ b/libprocessgroup/profiles/task_profiles_28.json
@@ -117,7 +117,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_29.json b/libprocessgroup/profiles/task_profiles_29.json
index 52279b8..6174c8d 100644
--- a/libprocessgroup/profiles/task_profiles_29.json
+++ b/libprocessgroup/profiles/task_profiles_29.json
@@ -117,7 +117,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/profiles/task_profiles_30.json b/libprocessgroup/profiles/task_profiles_30.json
index 56053e0..e7be548 100644
--- a/libprocessgroup/profiles/task_profiles_30.json
+++ b/libprocessgroup/profiles/task_profiles_30.json
@@ -117,7 +117,19 @@
         }
       ]
     },
-
+    {
+      "Name": "Dex2oatPerformance",
+      "Actions": [
+        {
+          "Name": "JoinCgroup",
+          "Params":
+          {
+            "Controller": "schedtune",
+            "Path": "background"
+          }
+        }
+      ]
+    },
     {
       "Name": "CpuPolicySpread",
       "Actions": [
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index cf74e65..e935f99 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -194,22 +194,39 @@
     fd_.reset(FDS_NOT_CACHED);
 }
 
-bool SetCgroupAction::AddTidToCgroup(int tid, int fd) {
+bool SetCgroupAction::AddTidToCgroup(int tid, int fd, const char* controller_name) {
     if (tid <= 0) {
         return true;
     }
 
     std::string value = std::to_string(tid);
 
-    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) < 0) {
-        // If the thread is in the process of exiting, don't flag an error
-        if (errno != ESRCH) {
-            PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
-            return false;
-        }
+    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) {
+        return true;
     }
 
-    return true;
+    // If the thread is in the process of exiting, don't flag an error
+    if (errno == ESRCH) {
+        return true;
+    }
+
+    // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus
+    if (errno == ENOSPC && !strcmp(controller_name, "cpuset")) {
+        // This is an abnormal case happening only in testing, so report it only once
+        static bool empty_cpuset_reported = false;
+
+        if (empty_cpuset_reported) {
+            return true;
+        }
+
+        LOG(ERROR) << "Failed to add task '" << value
+                   << "' into cpuset because all cpus in that cpuset are offline";
+        empty_cpuset_reported = true;
+    } else {
+        PLOG(ERROR) << "AddTidToCgroup failed to write '" << value << "'; fd=" << fd;
+    }
+
+    return false;
 }
 
 bool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
@@ -219,7 +236,7 @@
         PLOG(WARNING) << "Failed to open " << procs_path;
         return false;
     }
-    if (!AddTidToCgroup(pid, tmp_fd)) {
+    if (!AddTidToCgroup(pid, tmp_fd, controller()->name())) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
@@ -231,7 +248,7 @@
     std::lock_guard<std::mutex> lock(fd_mutex_);
     if (IsFdValid()) {
         // fd is cached, reuse it
-        if (!AddTidToCgroup(tid, fd_)) {
+        if (!AddTidToCgroup(tid, fd_, controller()->name())) {
             LOG(ERROR) << "Failed to add task into cgroup";
             return false;
         }
@@ -256,7 +273,7 @@
         PLOG(WARNING) << "Failed to open " << tasks_path << ": " << strerror(errno);
         return false;
     }
-    if (!AddTidToCgroup(tid, tmp_fd)) {
+    if (!AddTidToCgroup(tid, tmp_fd, controller()->name())) {
         LOG(ERROR) << "Failed to add task into cgroup";
         return false;
     }
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 25a84b0..97c38f4 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -134,7 +134,7 @@
     mutable std::mutex fd_mutex_;
 
     static bool IsAppDependentPath(const std::string& path);
-    static bool AddTidToCgroup(int tid, int fd);
+    static bool AddTidToCgroup(int tid, int fd, const char* controller_name);
 
     bool IsFdValid() const { return fd_ > FDS_INACCESSIBLE; }
 };
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 3b6cfd8..515cc10 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -31,14 +31,41 @@
 #include <netinet/in.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/personality.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <sys/utsname.h>
+
+#include <android-base/parseint.h>
+#include <log/log.h>
+#include <sysutils/NetlinkEvent.h>
+
+using android::base::ParseInt;
 
 /* From kernel's net/netfilter/xt_quota2.c */
 const int LOCAL_QLOG_NL_EVENT = 112;
 const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
 
-/* From deprecated ipt_ULOG.h to parse QLOG_NL_EVENT. */
+/******************************************************************************
+ * WARNING: HERE BE DRAGONS!                                                  *
+ *                                                                            *
+ * This is here to provide for compatibility with both 32 and 64-bit kernels  *
+ * from 32-bit userspace.                                                     *
+ *                                                                            *
+ * The kernel definition of this struct uses types (like long) that are not   *
+ * the same across 32-bit and 64-bit builds, and there is no compatibility    *
+ * layer to fix it up before it reaches userspace.                            *
+ * As such we need to detect the bit-ness of the kernel and deal with it.     *
+ *                                                                            *
+ ******************************************************************************/
+
+/*
+ * This is the verbatim kernel declaration from net/netfilter/xt_quota2.c,
+ * it is *NOT* of a well defined layout and is included here for compile
+ * time assertions only.
+ *
+ * It got there from deprecated ipt_ULOG.h to parse QLOG_NL_EVENT.
+ */
 #define ULOG_MAC_LEN 80
 #define ULOG_PREFIX_LEN 32
 typedef struct ulog_packet_msg {
@@ -55,11 +82,117 @@
     unsigned char payload[0];
 } ulog_packet_msg_t;
 
-#include <android-base/parseint.h>
-#include <log/log.h>
-#include <sysutils/NetlinkEvent.h>
+// On Linux int is always 32 bits, while sizeof(long) == sizeof(void*),
+// thus long on a 32-bit Linux kernel is 32-bits, like int always is
+typedef int long32;
+typedef unsigned int ulong32;
+static_assert(sizeof(long32) == 4);
+static_assert(sizeof(ulong32) == 4);
 
-using android::base::ParseInt;
+// Here's the same structure definition with the assumption the kernel
+// is compiled for 32-bits.
+typedef struct {
+    ulong32 mark;
+    long32 timestamp_sec;
+    long32 timestamp_usec;
+    unsigned int hook;
+    char indev_name[IFNAMSIZ];
+    char outdev_name[IFNAMSIZ];
+    ulong32 data_len;
+    char prefix[ULOG_PREFIX_LEN];
+    unsigned char mac_len;
+    unsigned char mac[ULOG_MAC_LEN];
+    unsigned char payload[0];
+} ulog_packet_msg32_t;
+
+// long on a 64-bit kernel is 64-bits with 64-bit alignment,
+// while long long is 64-bit but may have 32-bit aligment.
+typedef long long __attribute__((__aligned__(8))) long64;
+typedef unsigned long long __attribute__((__aligned__(8))) ulong64;
+static_assert(sizeof(long64) == 8);
+static_assert(sizeof(ulong64) == 8);
+
+// Here's the same structure definition with the assumption the kernel
+// is compiled for 64-bits.
+typedef struct {
+    ulong64 mark;
+    long64 timestamp_sec;
+    long64 timestamp_usec;
+    unsigned int hook;
+    char indev_name[IFNAMSIZ];
+    char outdev_name[IFNAMSIZ];
+    ulong64 data_len;
+    char prefix[ULOG_PREFIX_LEN];
+    unsigned char mac_len;
+    unsigned char mac[ULOG_MAC_LEN];
+    unsigned char payload[0];
+} ulog_packet_msg64_t;
+
+// One expects the 32-bit version to be smaller than the 64-bit version.
+static_assert(sizeof(ulog_packet_msg32_t) < sizeof(ulog_packet_msg64_t));
+// And either way the 'native' version should match either the 32 or 64 bit one.
+static_assert(sizeof(ulog_packet_msg_t) == sizeof(ulog_packet_msg32_t) ||
+              sizeof(ulog_packet_msg_t) == sizeof(ulog_packet_msg64_t));
+
+// In practice these sizes are always simply (for both x86 and arm):
+static_assert(sizeof(ulog_packet_msg32_t) == 168);
+static_assert(sizeof(ulog_packet_msg64_t) == 192);
+
+// Figure out the bitness of userspace.
+// Trivial and known at compile time.
+static bool isUserspace64bit(void) {
+    return sizeof(long) == 8;
+}
+
+// Figure out the bitness of the kernel.
+static bool isKernel64Bit(void) {
+    // a 64-bit userspace requires a 64-bit kernel
+    if (isUserspace64bit()) return true;
+
+    static bool init = false;
+    static bool cache = false;
+    if (init) return cache;
+
+    // Retrieve current personality - on Linux this system call *cannot* fail.
+    int p = personality(0xffffffff);
+    // But if it does just assume kernel and userspace (which is 32-bit) match...
+    if (p == -1) return false;
+
+    // This will effectively mask out the bottom 8 bits, and switch to 'native'
+    // personality, and then return the previous personality of this thread
+    // (likely PER_LINUX or PER_LINUX32) with any extra options unmodified.
+    int q = personality((p & ~PER_MASK) | PER_LINUX);
+    // Per man page this theoretically could error out with EINVAL,
+    // but kernel code analysis suggests setting PER_LINUX cannot fail.
+    // Either way, assume kernel and userspace (which is 32-bit) match...
+    if (q != p) return false;
+
+    struct utsname u;
+    (void)uname(&u);  // only possible failure is EFAULT, but u is on stack.
+
+    // Switch back to previous personality.
+    // Theoretically could fail with EINVAL on arm64 with no 32-bit support,
+    // but then we wouldn't have fetched 'p' from the kernel in the first place.
+    // Either way there's nothing meaningul we can do in case of error.
+    // Since PER_LINUX32 vs PER_LINUX only affects uname.machine it doesn't
+    // really hurt us either.  We're really just switching back to be 'clean'.
+    (void)personality(p);
+
+    // Possible values of utsname.machine observed on x86_64 desktop (arm via qemu):
+    //   x86_64 i686 aarch64 armv7l
+    // additionally observed on arm device:
+    //   armv8l
+    // presumably also might just be possible:
+    //   i386 i486 i586
+    // and there might be other weird arm32 cases.
+    // We note that the 64 is present in both 64-bit archs,
+    // and in general is likely to be present in only 64-bit archs.
+    cache = !!strstr(u.machine, "64");
+    init = true;
+    return cache;
+}
+
+/******************************************************************************/
 
 NetlinkEvent::NetlinkEvent() {
     mAction = Action::kUnknown;
@@ -280,13 +413,22 @@
  * Parse a QLOG_NL_EVENT message.
  */
 bool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) {
-    const char *devname;
-    ulog_packet_msg_t *pm = (ulog_packet_msg_t *) NLMSG_DATA(nh);
-    if (!checkRtNetlinkLength(nh, sizeof(*pm)))
-        return false;
+    const char* alert;
+    const char* devname;
 
-    devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;
-    asprintf(&mParams[0], "ALERT_NAME=%s", pm->prefix);
+    if (isKernel64Bit()) {
+        ulog_packet_msg64_t* pm64 = (ulog_packet_msg64_t*)NLMSG_DATA(nh);
+        if (!checkRtNetlinkLength(nh, sizeof(*pm64))) return false;
+        alert = pm64->prefix;
+        devname = pm64->indev_name[0] ? pm64->indev_name : pm64->outdev_name;
+    } else {
+        ulog_packet_msg32_t* pm32 = (ulog_packet_msg32_t*)NLMSG_DATA(nh);
+        if (!checkRtNetlinkLength(nh, sizeof(*pm32))) return false;
+        alert = pm32->prefix;
+        devname = pm32->indev_name[0] ? pm32->indev_name : pm32->outdev_name;
+    }
+
+    asprintf(&mParams[0], "ALERT_NAME=%s", alert);
     asprintf(&mParams[1], "INTERFACE=%s", devname);
     mSubsystem = strdup("qlog");
     mAction = Action::kChange;
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 3831693..27fa059 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -155,6 +155,7 @@
     mkdir /dev/cpuctl/rt
     mkdir /dev/cpuctl/system
     mkdir /dev/cpuctl/system-background
+    mkdir /dev/cpuctl/dex2oat
     chown system system /dev/cpuctl
     chown system system /dev/cpuctl/foreground
     chown system system /dev/cpuctl/background
@@ -162,6 +163,7 @@
     chown system system /dev/cpuctl/rt
     chown system system /dev/cpuctl/system
     chown system system /dev/cpuctl/system-background
+    chown system system /dev/cpuctl/dex2oat
     chown system system /dev/cpuctl/tasks
     chown system system /dev/cpuctl/foreground/tasks
     chown system system /dev/cpuctl/background/tasks
@@ -169,6 +171,7 @@
     chown system system /dev/cpuctl/rt/tasks
     chown system system /dev/cpuctl/system/tasks
     chown system system /dev/cpuctl/system-background/tasks
+    chown system system /dev/cpuctl/dex2oat/tasks
     chmod 0664 /dev/cpuctl/tasks
     chmod 0664 /dev/cpuctl/foreground/tasks
     chmod 0664 /dev/cpuctl/background/tasks
@@ -176,6 +179,7 @@
     chmod 0664 /dev/cpuctl/rt/tasks
     chmod 0664 /dev/cpuctl/system/tasks
     chmod 0664 /dev/cpuctl/system-background/tasks
+    chmod 0664 /dev/cpuctl/dex2oat/tasks
 
     # Create a cpu group for NNAPI HAL processes
     mkdir /dev/cpuctl/nnapi-hal
@@ -1097,6 +1101,9 @@
     # Define default initial receive window size in segments.
     setprop net.tcp_def_init_rwnd 60
 
+    # Update dm-verity state and set partition.*.verified properties.
+    verity_update_state
+
     # Start standard binderized HAL daemons
     class_start hal
 
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 7bd1d10..99d9e56 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -117,6 +117,7 @@
         "libkeymint",
         "liblog",
         "libtrusty",
+        "libutils",
     ],
     required: [
         "android.hardware.hardware_keystore.xml",
@@ -142,6 +143,7 @@
         "libtrusty",
         "libhardware",
         "libkeymaster_messages",
+        "libutils",
         "libxml2",
     ],
     export_include_dirs: ["include"],
@@ -169,6 +171,7 @@
         "libtrusty",
         "libhardware",
         "libkeymaster_messages",
+        "libutils",
         "libxml2",
     ],
     cflags: [
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 2d44009..db1a9f4 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -19,6 +19,7 @@
 // TODO: make this generic in libtrusty
 
 #include <errno.h>
+#include <poll.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/uio.h>
@@ -33,11 +34,15 @@
 
 #include <trusty_keymaster/ipc/keymaster_ipc.h>
 #include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>
+#include <utils/Timers.h>
 
 #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0"
 
 static int handle_ = -1;
 
+static const int timeout_ms = 10 * 1000;
+static const int max_timeout_ms = 60 * 1000;
+
 int trusty_keymaster_connect() {
     int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT);
     if (rc < 0) {
@@ -84,7 +89,38 @@
     msg->cmd = cmd;
     memcpy(msg->payload, in, in_size);
 
+    nsecs_t start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);
+    bool timed_out = false;
+    int poll_timeout_ms = timeout_ms;
+    while (true) {
+        struct pollfd pfd;
+        pfd.fd = handle_;
+        pfd.events = POLLOUT;
+        pfd.revents = 0;
+
+        int p = poll(&pfd, 1, poll_timeout_ms);
+        if (p == 0) {
+            ALOGW("write for cmd %d is taking more than %lld nsecs", cmd,
+                  (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+            timed_out = true;
+            poll_timeout_ms *= 2;
+            if (poll_timeout_ms > max_timeout_ms) {
+                poll_timeout_ms = max_timeout_ms;
+            }
+            continue;
+        } else if (p < 0) {
+            ALOGE("write poll error: %d", errno);
+        } else if (pfd.revents != POLLOUT) {
+            ALOGW("unexpected poll() result: %d", pfd.revents);
+        }
+        break;
+    }
+
     ssize_t rc = write(handle_, msg, msg_size);
+    if (timed_out) {
+        ALOGW("write for cmd %d finished after %lld nsecs", cmd,
+              (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+    }
     free(msg);
 
     if (rc < 0) {
@@ -122,8 +158,37 @@
             return -EOVERFLOW;
         }
         iov[1] = {.iov_base = write_pos, .iov_len = buffer_size};
+        start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);
+        timed_out = false;
+        poll_timeout_ms = timeout_ms;
+        while (true) {
+            struct pollfd pfd;
+            pfd.fd = handle_;
+            pfd.events = POLLIN;
+            pfd.revents = 0;
 
+            int p = poll(&pfd, 1, poll_timeout_ms);
+            if (p == 0) {
+                ALOGW("readv for cmd %d is taking more than %lld nsecs", cmd,
+                      (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+                timed_out = true;
+                poll_timeout_ms *= 2;
+                if (poll_timeout_ms > max_timeout_ms) {
+                    poll_timeout_ms = max_timeout_ms;
+                }
+                continue;
+            } else if (p < 0) {
+                ALOGE("read poll error: %d", errno);
+            } else if (pfd.revents != POLLIN) {
+                ALOGW("unexpected poll() result: %d", pfd.revents);
+            }
+            break;
+        }
         rc = readv(handle_, iov, 2);
+        if (timed_out) {
+            ALOGW("readv for cmd %d finished after %lld nsecs", cmd,
+                  (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));
+        }
         if (rc < 0) {
             ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT,
                   strerror(errno));
diff --git a/trusty/keymaster/keymint/service.cpp b/trusty/keymaster/keymint/service.cpp
index 4060278..d5a77fb 100644
--- a/trusty/keymaster/keymint/service.cpp
+++ b/trusty/keymaster/keymint/service.cpp
@@ -31,7 +31,7 @@
 
 template <typename T, class... Args>
 std::shared_ptr<T> addService(Args&&... args) {
-    std::shared_ptr<T> service = std::make_shared<T>(std::forward<Args>(args)...);
+    std::shared_ptr<T> service = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
     auto instanceName = std::string(T::descriptor) + "/default";
     LOG(ERROR) << "Adding service instance: " << instanceName;
     auto status = AServiceManager_addService(service->asBinder().get(), instanceName.c_str());