Merge "Fix scudo MTE tests."
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 35be2bf..92e7675 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -38,6 +38,8 @@
 #include <unistd.h>
 
 #include <android-base/macros.h>
+#include <android-base/parsebool.h>
+#include <android-base/properties.h>
 #include <android-base/unique_fd.h>
 #include <async_safe/log.h>
 #include <bionic/reserved_signals.h>
@@ -49,7 +51,10 @@
 
 #include "handler/fallback.h"
 
-using android::base::Pipe;
+using ::android::base::GetBoolProperty;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
+using ::android::base::Pipe;
 
 // We muck with our fds in a 'thread' that doesn't share the same fd table.
 // Close fds in that thread with a raw close syscall instead of going through libc.
@@ -82,6 +87,13 @@
   return syscall(__NR_gettid);
 }
 
+static bool is_permissive_mte() {
+  // Environment variable for testing or local use from shell.
+  char* permissive_env = getenv("MTE_PERMISSIVE");
+  return GetBoolProperty("persist.sys.mte.permissive", false) ||
+         (permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
+}
+
 static inline void futex_wait(volatile void* ftx, int value) {
   syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
 }
@@ -592,7 +604,28 @@
     // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
     // starting to dump right before our death.
     pthread_mutex_unlock(&crash_mutex);
-  } else {
+  }
+#ifdef __aarch64__
+  else if (info->si_signo == SIGSEGV &&
+           (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) &&
+           is_permissive_mte()) {
+    // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread,
+    // and then let the failing instruction be retried. The second time should work (except
+    // if there is another non-MTE fault).
+    int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+    if (tagged_addr_ctrl < 0) {
+      fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
+    }
+    tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
+    if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
+      fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
+    }
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
+    pthread_mutex_unlock(&crash_mutex);
+  }
+#endif
+  else {
     // Resend the signal, so that either the debugger or the parent's waitpid sees it.
     resend_signal(info);
   }
diff --git a/debuggerd/test_permissive_mte/Android.bp b/debuggerd/test_permissive_mte/Android.bp
new file mode 100644
index 0000000..1c09240
--- /dev/null
+++ b/debuggerd/test_permissive_mte/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2022 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_binary {
+  name: "mte_crash",
+  srcs: ["mte_crash.cpp"],
+  sanitize: {
+    memtag_heap: true,
+    diag: {
+      memtag_heap: true,
+    },
+  },
+}
+
+java_test_host {
+    name: "permissive_mte_test",
+    libs: ["tradefed"],
+    static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
+    srcs:  ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"],
+    data: [":mte_crash"],
+    test_config: "AndroidTest.xml",
+    test_suites: ["general-tests"],
+}
diff --git a/debuggerd/test_permissive_mte/AndroidTest.xml b/debuggerd/test_permissive_mte/AndroidTest.xml
new file mode 100644
index 0000000..bd3d018
--- /dev/null
+++ b/debuggerd/test_permissive_mte/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+<configuration description="Runs the permissive MTE tests">
+    <option name="test-suite-tag" value="init_test_upgrade_mte" />
+    <option name="test-suite-tag" value="apct" />
+
+    <!-- For tombstone inspection. -->
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+      <option name="cleanup" value="true" />
+      <option name="push" value="mte_crash->/data/local/tmp/mte_crash" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.HostTest" >
+        <option name="jar" value="permissive_mte_test.jar" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/debuggerd/test_permissive_mte/mte_crash.cpp b/debuggerd/test_permissive_mte/mte_crash.cpp
new file mode 100644
index 0000000..97ad73f
--- /dev/null
+++ b/debuggerd/test_permissive_mte/mte_crash.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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 <stdio.h>
+#include <stdlib.h>
+
+int main(int, char**) {
+  volatile char* f = (char*)malloc(1);
+  printf("%c\n", f[17]);
+  return 0;
+}
diff --git a/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java b/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
new file mode 100644
index 0000000..5ff2b5b
--- /dev/null
+++ b/debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+package com.android.tests.init;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.server.os.TombstoneProtos.Tombstone;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.util.CommandResult;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Arrays;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class PermissiveMteTest extends BaseHostJUnit4Test {
+  String mUUID;
+
+  @Before
+  public void setUp() throws Exception {
+    mUUID = java.util.UUID.randomUUID().toString();
+    CommandResult result =
+        getDevice().executeShellV2Command("/data/local/tmp/mte_crash setUp " + mUUID);
+    assumeTrue("mte_crash needs to segfault", result.getExitCode() == 139);
+  }
+
+  Tombstone parseTombstone(String tombstonePath) throws Exception {
+    File tombstoneFile = getDevice().pullFile(tombstonePath);
+    InputStream istr = new FileInputStream(tombstoneFile);
+    Tombstone tombstoneProto;
+    try {
+      tombstoneProto = Tombstone.parseFrom(istr);
+    } finally {
+      istr.close();
+    }
+    return tombstoneProto;
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    String[] tombstones = getDevice().getChildren("/data/tombstones");
+    for (String tombstone : tombstones) {
+      if (!tombstone.endsWith(".pb")) {
+        continue;
+      }
+      String tombstonePath = "/data/tombstones/" + tombstone;
+      Tombstone tombstoneProto = parseTombstone(tombstonePath);
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
+        continue;
+      }
+      getDevice().deleteFile(tombstonePath);
+      // remove the non .pb file as well.
+      getDevice().deleteFile(tombstonePath.substring(0, tombstonePath.length() - 3));
+    }
+  }
+
+  @Test
+  public void testCrash() throws Exception {
+    CommandResult result = getDevice().executeShellV2Command(
+        "MTE_PERMISSIVE=1 /data/local/tmp/mte_crash testCrash " + mUUID);
+    assertThat(result.getExitCode()).isEqualTo(0);
+    int numberTombstones = 0;
+    String[] tombstones = getDevice().getChildren("/data/tombstones");
+    for (String tombstone : tombstones) {
+      if (!tombstone.endsWith(".pb")) {
+        continue;
+      }
+      String tombstonePath = "/data/tombstones/" + tombstone;
+      Tombstone tombstoneProto = parseTombstone(tombstonePath);
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
+        continue;
+      }
+      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains("testCrash"))) {
+        continue;
+      }
+      numberTombstones++;
+    }
+    assertThat(numberTombstones).isEqualTo(1);
+  }
+}
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 5c07eb0..22665ed 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -190,6 +190,7 @@
         "libhealthhalutils",
         "libhealthshim",
         "libsnapshot_cow",
+        "liblz4",
         "libsnapshot_nobinder",
         "update_metadata-protos",
     ],
@@ -254,6 +255,7 @@
         "libsparse",
         "libutils",
         "liblog",
+        "liblz4",
         "libz",
         "libdiagnose_usb",
         "libbase",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 4ea68da..b732c76 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -79,3 +79,4 @@
 #define FB_VAR_SECURITY_PATCH_LEVEL "security-patch-level"
 #define FB_VAR_TREBLE_ENABLED "treble-enabled"
 #define FB_VAR_MAX_FETCH_SIZE "max-fetch-size"
+#define FB_VAR_DMESG "dmesg"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 0f3a208..524196c 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -112,52 +112,65 @@
     }
 }
 
-bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
-    const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
-            {FB_VAR_VERSION, {GetVersion, nullptr}},
-            {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
-            {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
-            {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
-            {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
-            {FB_VAR_PRODUCT, {GetProduct, nullptr}},
-            {FB_VAR_SERIALNO, {GetSerial, nullptr}},
-            {FB_VAR_VARIANT, {GetVariant, nullptr}},
-            {FB_VAR_SECURE, {GetSecure, nullptr}},
-            {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
-            {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
-            {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
-            {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
-            {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
-            {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
-            {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
-            {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
-            {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
-            {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
-            {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
-            {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
-            {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
-            {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
-            {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
-            {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
-            {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
-            {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
-            {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
-            {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
-            {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
-            {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
-            {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
-            {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
-            {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
-    };
+const std::unordered_map<std::string, VariableHandlers> kVariableMap = {
+        {FB_VAR_VERSION, {GetVersion, nullptr}},
+        {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},
+        {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},
+        {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},
+        {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},
+        {FB_VAR_PRODUCT, {GetProduct, nullptr}},
+        {FB_VAR_SERIALNO, {GetSerial, nullptr}},
+        {FB_VAR_VARIANT, {GetVariant, nullptr}},
+        {FB_VAR_SECURE, {GetSecure, nullptr}},
+        {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},
+        {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},
+        {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},
+        {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},
+        {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},
+        {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},
+        {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},
+        {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},
+        {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},
+        {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},
+        {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},
+        {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},
+        {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},
+        {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},
+        {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},
+        {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},
+        {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},
+        {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},
+        {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},
+        {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},
+        {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},
+        {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},
+        {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},
+        {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},
+        {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},
+};
 
+static bool GetVarAll(FastbootDevice* device) {
+    for (const auto& [name, handlers] : kVariableMap) {
+        GetAllVars(device, name, handlers);
+    }
+    return true;
+}
+
+const std::unordered_map<std::string, std::function<bool(FastbootDevice*)>> kSpecialVars = {
+        {"all", GetVarAll},
+        {"dmesg", GetDmesg},
+};
+
+bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
     if (args.size() < 2) {
         return device->WriteFail("Missing argument");
     }
 
-    // Special case: return all variables that we can.
-    if (args[1] == "all") {
-        for (const auto& [name, handlers] : kVariableMap) {
-            GetAllVars(device, name, handlers);
+    // "all" and "dmesg" are multiline and handled specially.
+    auto found_special = kSpecialVars.find(args[1]);
+    if (found_special != kSpecialVars.end()) {
+        if (!found_special->second(device)) {
+            return false;
         }
         return device->WriteOkay("");
     }
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index 0cf4699..0de00b1 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -17,6 +17,7 @@
 #include "variables.h"
 
 #include <inttypes.h>
+#include <stdio.h>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -28,6 +29,7 @@
 #include <fs_mgr.h>
 #include <liblp/liblp.h>
 
+#include "constants.h"
 #include "fastboot_device.h"
 #include "flashing.h"
 #include "utility.h"
@@ -46,6 +48,7 @@
 using ::android::hardware::fastboot::V1_0::Status;
 using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl;
 using namespace android::fs_mgr;
+using namespace std::string_literals;
 
 constexpr char kFastbootProtocolVersion[] = "0.4";
 
@@ -518,3 +521,36 @@
     *message = android::base::StringPrintf("0x%X", kMaxFetchSizeDefault);
     return true;
 }
+
+bool GetDmesg(FastbootDevice* device) {
+    if (GetDeviceLockStatus()) {
+        return device->WriteFail("Cannot use when device flashing is locked");
+    }
+
+    std::unique_ptr<FILE, decltype(&::fclose)> fp(popen("/system/bin/dmesg", "re"), ::fclose);
+    if (!fp) {
+        PLOG(ERROR) << "popen /system/bin/dmesg";
+        return device->WriteFail("Unable to run dmesg: "s + strerror(errno));
+    }
+
+    ssize_t rv;
+    size_t n = 0;
+    char* str = nullptr;
+    while ((rv = ::getline(&str, &n, fp.get())) > 0) {
+        if (str[rv - 1] == '\n') {
+            rv--;
+        }
+        device->WriteInfo(std::string(str, rv));
+    }
+
+    int saved_errno = errno;
+    ::free(str);
+
+    if (rv < 0 && saved_errno) {
+        LOG(ERROR) << "dmesg getline: " << strerror(saved_errno);
+        device->WriteFail("Unable to read dmesg: "s + strerror(saved_errno));
+        return false;
+    }
+
+    return true;
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index f40a025..aa4d9fc 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -83,6 +83,9 @@
 bool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,
                      std::string* message);
 
+// Complex cases.
+bool GetDmesg(FastbootDevice* device);
+
 // Helpers for getvar all.
 std::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);
 std::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);
diff --git a/fastboot/vendor_boot_img_utils.cpp b/fastboot/vendor_boot_img_utils.cpp
index 9e09abb..9f05253 100644
--- a/fastboot/vendor_boot_img_utils.cpp
+++ b/fastboot/vendor_boot_img_utils.cpp
@@ -152,6 +152,9 @@
     if (memcmp(hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE) != 0) {
         return Errorf("Vendor boot image magic mismatch");
     }
+    if (hdr->page_size == 0) {
+        return Errorf("Page size cannot be zero");
+    }
     if (hdr->header_version < version) {
         return Errorf("Require vendor boot header V{} but is V{}", version, hdr->header_version);
     }
@@ -199,6 +202,7 @@
 }
 
 // round |value| up to a multiple of |page_size|.
+// aware that this can be integer overflow if value is too large
 inline uint32_t round_up(uint32_t value, uint32_t page_size) {
     return (value + page_size - 1) / page_size * page_size;
 }
@@ -311,7 +315,13 @@
     const uint32_t r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);
     const uint32_t s = round_up(hdr->bootconfig_size, hdr->page_size);
 
-    if (hdr->vendor_ramdisk_table_entry_num == std::numeric_limits<uint32_t>::max()) {
+    uint64_t total_size = (uint64_t)o + p + q + r + s;
+    if (total_size > vendor_boot.size()) {
+        return Errorf("Vendor boot image size is too small, overflow");
+    }
+
+    if ((uint64_t)hdr->vendor_ramdisk_table_entry_num * sizeof(vendor_ramdisk_table_entry_v4) >
+        (uint64_t)o + p + q + r) {
         return Errorf("Too many vendor ramdisk entries in table, overflow");
     }
 
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index c12a672..c0e3161 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -90,9 +90,6 @@
 #define SYSFS_EXT4_VERITY "/sys/fs/ext4/features/verity"
 #define SYSFS_EXT4_CASEFOLD "/sys/fs/ext4/features/casefold"
 
-// FIXME: this should be in system/extras
-#define EXT4_FEATURE_COMPAT_STABLE_INODES 0x0800
-
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
 
 using android::base::Basename;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 06368b8..382d0c8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -539,6 +539,20 @@
     return false;
 }
 
+template <typename Pred>
+std::vector<FstabEntry*> GetEntriesByPred(Fstab* fstab, const Pred& pred) {
+    if (fstab == nullptr) {
+        return {};
+    }
+    std::vector<FstabEntry*> entries;
+    for (auto&& entry : *fstab) {
+        if (pred(entry)) {
+            entries.push_back(&entry);
+        }
+    }
+    return entries;
+}
+
 }  // namespace
 
 bool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) {
@@ -614,11 +628,7 @@
         userdata = BuildDsuUserdataFstabEntry();
     }
 
-    if (EraseFstabEntry(fstab, "/data")) {
-        fstab->emplace_back(userdata);
-    }
-
-    // Convert others
+    // Convert RO partitions.
     for (auto&& partition : dsu_partitions) {
         if (!EndsWith(partition, gsi::kDsuPostfix)) {
             continue;
@@ -638,47 +648,58 @@
         //    vendor_gsi for vendor
         std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));
         std::string mount_point = "/" + lp_name;
-        std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, mount_point);
-        if (entries.empty()) {
-            FstabEntry entry = {
-                    .blk_device = partition,
-                    // .logical_partition_name is required to look up AVB Hashtree descriptors.
-                    .logical_partition_name = "system",
-                    .mount_point = mount_point,
-                    .fs_type = "ext4",
-                    .flags = MS_RDONLY,
-                    .fs_options = "barrier=1",
-                    .avb_keys = kDsuKeysDir,
-            };
-            entry.fs_mgr_flags.wait = true;
-            entry.fs_mgr_flags.logical = true;
-            entry.fs_mgr_flags.first_stage_mount = true;
-            fstab->emplace_back(entry);
-        } else {
-            // If the corresponding partition exists, transform all its Fstab
-            // by pointing .blk_device to the DSU partition.
-            for (auto&& entry : entries) {
-                entry->blk_device = partition;
-                // AVB keys for DSU should always be under kDsuKeysDir.
-                entry->avb_keys = kDsuKeysDir;
-                entry->fs_mgr_flags.logical = true;
-            }
-            // Make sure the ext4 is included to support GSI.
-            auto partition_ext4 =
-                    std::find_if(fstab->begin(), fstab->end(), [&](const auto& entry) {
-                        return entry.mount_point == mount_point && entry.fs_type == "ext4";
-                    });
-            if (partition_ext4 == fstab->end()) {
-                auto new_entry = *GetEntryForMountPoint(fstab, mount_point);
-                new_entry.fs_type = "ext4";
-                auto it = std::find_if(fstab->rbegin(), fstab->rend(),
-                                       [&mount_point](const auto& entry) {
-                                           return entry.mount_point == mount_point;
-                                       });
-                auto end_of_mount_point_group = fstab->begin() + std::distance(it, fstab->rend());
-                fstab->insert(end_of_mount_point_group, new_entry);
+
+        // List of fs_type entries we're lacking, need to synthesis these later.
+        std::vector<std::string> lack_fs_list = {"ext4", "erofs"};
+
+        // Only support early mount (first_stage_mount) partitions.
+        auto pred = [&mount_point](const FstabEntry& entry) {
+            return entry.fs_mgr_flags.first_stage_mount && entry.mount_point == mount_point;
+        };
+
+        // Transform all matching entries and assume they are all adjacent for simplicity.
+        for (auto&& entry : GetEntriesByPred(fstab, pred)) {
+            // .blk_device is replaced with the DSU partition.
+            entry->blk_device = partition;
+            // .avb_keys hints first_stage_mount to load the chained-vbmeta image from partition
+            // footer. See aosp/932779 for more details.
+            entry->avb_keys = kDsuKeysDir;
+            // .logical_partition_name is required to look up AVB Hashtree descriptors.
+            entry->logical_partition_name = lp_name;
+            entry->fs_mgr_flags.logical = true;
+            entry->fs_mgr_flags.slot_select = false;
+            entry->fs_mgr_flags.slot_select_other = false;
+
+            if (auto it = std::find(lack_fs_list.begin(), lack_fs_list.end(), entry->fs_type);
+                it != lack_fs_list.end()) {
+                lack_fs_list.erase(it);
             }
         }
+
+        if (!lack_fs_list.empty()) {
+            // Insert at the end of the existing mountpoint group, or at the end of fstab.
+            // We assume there is at most one matching mountpoint group, which is the common case.
+            auto it = std::find_if_not(std::find_if(fstab->begin(), fstab->end(), pred),
+                                       fstab->end(), pred);
+            for (const auto& fs_type : lack_fs_list) {
+                it = std::next(fstab->insert(it, {.blk_device = partition,
+                                                  .logical_partition_name = lp_name,
+                                                  .mount_point = mount_point,
+                                                  .fs_type = fs_type,
+                                                  .flags = MS_RDONLY,
+                                                  .avb_keys = kDsuKeysDir,
+                                                  .fs_mgr_flags{
+                                                          .wait = true,
+                                                          .logical = true,
+                                                          .first_stage_mount = true,
+                                                  }}));
+            }
+        }
+    }
+
+    // Always append userdata last for stable ordering.
+    if (EraseFstabEntry(fstab, "/data")) {
+        fstab->emplace_back(userdata);
     }
 }
 
@@ -721,22 +742,13 @@
     }
     if (!is_proc_mounts) {
         if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
-            // This is expected to fail if host is android Q, since Q doesn't
-            // support DSU slotting. The DSU "active" indicator file would be
-            // non-existent or empty if DSU is enabled within the guest system.
-            // In that case, just use the default slot name "dsu".
             std::string dsu_slot;
-            if (!android::gsi::GetActiveDsu(&dsu_slot) && errno != ENOENT) {
+            if (!android::gsi::GetActiveDsu(&dsu_slot)) {
                 PERROR << __FUNCTION__ << "(): failed to get active DSU slot";
                 return false;
             }
-            if (dsu_slot.empty()) {
-                dsu_slot = "dsu";
-                LWARNING << __FUNCTION__ << "(): assuming default DSU slot: " << dsu_slot;
-            }
-            // This file is non-existent on Q vendor.
             std::string lp_names;
-            if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names) && errno != ENOENT) {
+            if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names)) {
                 PERROR << __FUNCTION__ << "(): failed to read DSU LP names";
                 return false;
             }
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 6db8f13..dffb2e7 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -44,7 +44,6 @@
         "libext2_uuid",
         "libext4_utils",
         "libfstab",
-        "libsnapshot_cow",
         "libsnapshot_snapuserd",
         "libz",
     ],
@@ -154,22 +153,6 @@
         "-Wall",
         "-Werror",
     ],
-    export_include_dirs: ["include"],
-    srcs: [
-        "cow_decompress.cpp",
-        "cow_reader.cpp",
-        "cow_writer.cpp",
-        "cow_format.cpp",
-    ],
-}
-
-cc_library_static {
-    name: "libsnapshot_cow",
-    defaults: [
-        "libsnapshot_cow_defaults",
-    ],
-    host_supported: true,
-    recovery_available: true,
     shared_libs: [
         "libbase",
         "liblog",
@@ -177,7 +160,24 @@
     static_libs: [
         "libbrotli",
         "libz",
+        "liblz4",
     ],
+    export_include_dirs: ["include"],
+}
+
+cc_library_static {
+    name: "libsnapshot_cow",
+    defaults: [
+        "libsnapshot_cow_defaults",
+    ],
+    srcs: [
+        "cow_decompress.cpp",
+        "cow_reader.cpp",
+        "cow_writer.cpp",
+        "cow_format.cpp",
+    ],
+    host_supported: true,
+    recovery_available: true,
     ramdisk_available: true,
     vendor_ramdisk_available: true,
 }
@@ -214,7 +214,7 @@
 
 cc_defaults {
     name: "libsnapshot_test_defaults",
-    defaults: ["libsnapshot_defaults"],
+    defaults: ["libsnapshot_defaults", "libsnapshot_cow_defaults"],
     srcs: [
         "partition_cow_creator_test.cpp",
         "snapshot_metadata_updater_test.cpp",
@@ -295,6 +295,7 @@
 
 cc_binary {
     name: "snapshotctl",
+    defaults: ["libsnapshot_cow_defaults", "libsnapshot_hal_deps"],
     srcs: [
         "snapshotctl.cpp",
     ],
@@ -343,6 +344,9 @@
 
 cc_defaults {
     name: "libsnapshot_fuzzer_defaults",
+    defaults: [
+        "libsnapshot_cow_defaults",
+    ],
     native_coverage : true,
     srcs: [
         // Compile the protobuf definition again with type full.
@@ -416,6 +420,7 @@
     name: "cow_api_test",
     defaults: [
         "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
     ],
     srcs: [
         "cow_api_test.cpp",
@@ -471,6 +476,7 @@
         "libsparse",
         "libxz",
         "libz",
+        "liblz4",
         "libziparchive",
         "update_metadata-protos",
     ],
@@ -486,6 +492,9 @@
 
 cc_binary {
     name: "estimate_cow_from_nonab_ota",
+    defaults: [
+        "libsnapshot_cow_defaults",
+    ],
     host_supported: true,
     device_supported: false,
     cflags: [
@@ -519,6 +528,7 @@
     name: "inspect_cow",
     host_supported: true,
     device_supported: true,
+    defaults: ["libsnapshot_cow_defaults"],
     cflags: [
         "-D_FILE_OFFSET_BITS=64",
         "-Wall",
diff --git a/fs_mgr/libsnapshot/cow_decompress.cpp b/fs_mgr/libsnapshot/cow_decompress.cpp
index faceafe..a4d2277 100644
--- a/fs_mgr/libsnapshot/cow_decompress.cpp
+++ b/fs_mgr/libsnapshot/cow_decompress.cpp
@@ -20,6 +20,7 @@
 
 #include <android-base/logging.h>
 #include <brotli/decode.h>
+#include <lz4.h>
 #include <zlib.h>
 
 namespace android {
@@ -260,5 +261,43 @@
     return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
 }
 
+class Lz4Decompressor final : public IDecompressor {
+  public:
+    ~Lz4Decompressor() override = default;
+
+    bool Decompress(const size_t output_size) override {
+        size_t actual_buffer_size = 0;
+        auto&& output_buffer = sink_->GetBuffer(output_size, &actual_buffer_size);
+        if (actual_buffer_size != output_size) {
+            LOG(ERROR) << "Failed to allocate buffer of size " << output_size << " only got "
+                       << actual_buffer_size << " bytes";
+            return false;
+        }
+        std::string input_buffer;
+        input_buffer.resize(stream_->Size());
+        size_t bytes_read = 0;
+        stream_->Read(input_buffer.data(), input_buffer.size(), &bytes_read);
+        if (bytes_read != input_buffer.size()) {
+            LOG(ERROR) << "Failed to read all input at once. Expected: " << input_buffer.size()
+                       << " actual: " << bytes_read;
+            return false;
+        }
+        const int bytes_decompressed =
+                LZ4_decompress_safe(input_buffer.data(), static_cast<char*>(output_buffer),
+                                    input_buffer.size(), output_size);
+        if (bytes_decompressed != output_size) {
+            LOG(ERROR) << "Failed to decompress LZ4 block, expected output size: " << output_size
+                       << ", actual: " << bytes_decompressed;
+            return false;
+        }
+        sink_->ReturnData(output_buffer, output_size);
+        return true;
+    }
+};
+
+std::unique_ptr<IDecompressor> IDecompressor::Lz4() {
+    return std::make_unique<Lz4Decompressor>();
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/cow_decompress.h b/fs_mgr/libsnapshot/cow_decompress.h
index f485256..7f74eda 100644
--- a/fs_mgr/libsnapshot/cow_decompress.h
+++ b/fs_mgr/libsnapshot/cow_decompress.h
@@ -41,6 +41,7 @@
     static std::unique_ptr<IDecompressor> Uncompressed();
     static std::unique_ptr<IDecompressor> Gz();
     static std::unique_ptr<IDecompressor> Brotli();
+    static std::unique_ptr<IDecompressor> Lz4();
 
     // |output_bytes| is the expected total number of bytes to sink.
     virtual bool Decompress(size_t output_bytes) = 0;
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 746feeb..653492c 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -775,6 +775,9 @@
         case kCowCompressBrotli:
             decompressor = IDecompressor::Brotli();
             break;
+        case kCowCompressLz4:
+            decompressor = IDecompressor::Lz4();
+            break;
         default:
             LOG(ERROR) << "Unknown compression type: " << op.compression;
             return false;
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index 5ce1d3b..7281fc2 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -24,8 +24,10 @@
 #include <android-base/logging.h>
 #include <android-base/unique_fd.h>
 #include <brotli/encode.h>
+#include <libsnapshot/cow_format.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
+#include <lz4.h>
 #include <zlib.h>
 
 namespace android {
@@ -129,6 +131,8 @@
         compression_ = kCowCompressGz;
     } else if (options_.compression == "brotli") {
         compression_ = kCowCompressBrotli;
+    } else if (options_.compression == "lz4") {
+        compression_ = kCowCompressLz4;
     } else if (options_.compression == "none") {
         compression_ = kCowCompressNone;
     } else if (!options_.compression.empty()) {
@@ -403,35 +407,56 @@
 std::basic_string<uint8_t> CowWriter::Compress(const void* data, size_t length) {
     switch (compression_) {
         case kCowCompressGz: {
-            auto bound = compressBound(length);
-            auto buffer = std::make_unique<uint8_t[]>(bound);
+            const auto bound = compressBound(length);
+            std::basic_string<uint8_t> buffer(bound, '\0');
 
             uLongf dest_len = bound;
-            auto rv = compress2(buffer.get(), &dest_len, reinterpret_cast<const Bytef*>(data),
+            auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data),
                                 length, Z_BEST_COMPRESSION);
             if (rv != Z_OK) {
                 LOG(ERROR) << "compress2 returned: " << rv;
                 return {};
             }
-            return std::basic_string<uint8_t>(buffer.get(), dest_len);
+            buffer.resize(dest_len);
+            return buffer;
         }
         case kCowCompressBrotli: {
-            auto bound = BrotliEncoderMaxCompressedSize(length);
+            const auto bound = BrotliEncoderMaxCompressedSize(length);
             if (!bound) {
                 LOG(ERROR) << "BrotliEncoderMaxCompressedSize returned 0";
                 return {};
             }
-            auto buffer = std::make_unique<uint8_t[]>(bound);
+            std::basic_string<uint8_t> buffer(bound, '\0');
 
             size_t encoded_size = bound;
             auto rv = BrotliEncoderCompress(
                     BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,
-                    reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.get());
+                    reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());
             if (!rv) {
                 LOG(ERROR) << "BrotliEncoderCompress failed";
                 return {};
             }
-            return std::basic_string<uint8_t>(buffer.get(), encoded_size);
+            buffer.resize(encoded_size);
+            return buffer;
+        }
+        case kCowCompressLz4: {
+            const auto bound = LZ4_compressBound(length);
+            if (!bound) {
+                LOG(ERROR) << "LZ4_compressBound returned 0";
+                return {};
+            }
+            std::basic_string<uint8_t> buffer(bound, '\0');
+
+            const auto compressed_size = LZ4_compress_default(
+                    static_cast<const char*>(data), reinterpret_cast<char*>(buffer.data()), length,
+                    buffer.size());
+            if (compressed_size <= 0) {
+                LOG(ERROR) << "LZ4_compress_default failed, input size: " << length
+                           << ", compression bound: " << bound << ", ret: " << compressed_size;
+                return {};
+            }
+            buffer.resize(compressed_size);
+            return buffer;
         }
         default:
             LOG(ERROR) << "unhandled compression type: " << compression_;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
index 9f4ddbb..ba75a8d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h
@@ -154,9 +154,12 @@
 static constexpr uint8_t kCowSequenceOp = 7;
 static constexpr uint8_t kCowFooterOp = -1;
 
-static constexpr uint8_t kCowCompressNone = 0;
-static constexpr uint8_t kCowCompressGz = 1;
-static constexpr uint8_t kCowCompressBrotli = 2;
+enum CowCompressionAlgorithm : uint8_t {
+    kCowCompressNone = 0,
+    kCowCompressGz = 1,
+    kCowCompressBrotli = 2,
+    kCowCompressLz4 = 3
+};
 
 static constexpr uint8_t kCowReadAheadNotStarted = 0;
 static constexpr uint8_t kCowReadAheadInProgress = 1;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index e17b5c6..e7a2f02 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -155,7 +155,7 @@
     android::base::borrowed_fd fd_;
     CowHeader header_{};
     CowFooter footer_{};
-    int compression_ = 0;
+    CowCompressionAlgorithm compression_ = kCowCompressNone;
     uint64_t next_op_pos_ = 0;
     uint64_t next_data_pos_ = 0;
     uint32_t cluster_size_ = 0;
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index e6db491..c8684a2 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -3273,8 +3273,21 @@
                 snapuserd_client_ = nullptr;
             }
         } else {
-            status.set_userspace_snapshots(!IsDmSnapshotTestingEnabled());
-            if (IsDmSnapshotTestingEnabled()) {
+            bool userSnapshotsEnabled = true;
+            const std::string UNKNOWN = "unknown";
+            const std::string vendor_release = android::base::GetProperty(
+                    "ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+            // No user-space snapshots if vendor partition is on Android 12
+            if (vendor_release.find("12") != std::string::npos) {
+                LOG(INFO) << "Userspace snapshots disabled as vendor partition is on Android: "
+                          << vendor_release;
+                userSnapshotsEnabled = false;
+            }
+
+            userSnapshotsEnabled = (userSnapshotsEnabled && !IsDmSnapshotTestingEnabled());
+            status.set_userspace_snapshots(userSnapshotsEnabled);
+            if (!userSnapshotsEnabled) {
                 is_snapshot_userspace_ = false;
                 LOG(INFO) << "User-space snapshots disabled for testing";
             } else {
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 36abf71..6a348b4 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -91,7 +91,7 @@
 
 void MountMetadata();
 bool ShouldUseCompression();
-bool ShouldUseUserspaceSnapshots();
+bool IsDaemonRequired();
 
 class SnapshotTest : public ::testing::Test {
   public:
@@ -1208,7 +1208,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
+    ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
     {
         // We should have started in SECOND_PHASE since nothing shrinks.
         ASSERT_TRUE(AcquireLock());
@@ -1342,7 +1342,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
+    ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
     {
         // Check that the merge phase is FIRST_PHASE until at least one call
         // to ProcessUpdateState() occurs.
@@ -1450,7 +1450,7 @@
 
     // Initiate the merge and wait for it to be completed.
     ASSERT_TRUE(init->InitiateMerge());
-    ASSERT_EQ(init->IsSnapuserdRequired(), ShouldUseUserspaceSnapshots());
+    ASSERT_EQ(init->IsSnapuserdRequired(), IsDaemonRequired());
     {
         // Check that the merge phase is FIRST_PHASE until at least one call
         // to ProcessUpdateState() occurs.
@@ -2750,13 +2750,26 @@
     }
 }
 
-bool ShouldUseUserspaceSnapshots() {
+bool IsDaemonRequired() {
     if (FLAGS_force_config == "dmsnap") {
         return false;
     }
+
+    const std::string UNKNOWN = "unknown";
+    const std::string vendor_release =
+            android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
+
+    // No userspace snapshots if vendor partition is on Android 12
+    // However, for GRF devices, snapuserd daemon will be on
+    // vendor ramdisk in Android 12.
+    if (vendor_release.find("12") != std::string::npos) {
+        return true;
+    }
+
     if (!FLAGS_force_config.empty()) {
         return true;
     }
+
     return IsUserspaceSnapshotsEnabled();
 }
 
diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp
index 57c599c..64e0b8a 100644
--- a/fs_mgr/libsnapshot/snapuserd/Android.bp
+++ b/fs_mgr/libsnapshot/snapuserd/Android.bp
@@ -87,6 +87,7 @@
         "liblog",
         "libsnapshot_cow",
         "libz",
+        "liblz4",
         "libext4_utils",
         "liburing",
     ],
@@ -145,6 +146,7 @@
     name: "cow_snapuserd_test",
     defaults: [
         "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
     ],
     srcs: [
         "dm-snapshot-merge/cow_snapuserd_test.cpp",
@@ -186,6 +188,7 @@
     name: "snapuserd_test",
     defaults: [
         "fs_mgr_defaults",
+        "libsnapshot_cow_defaults",
     ],
     srcs: [
         "user-space-merge/snapuserd_test.cpp",
diff --git a/fs_mgr/tests/Android.bp b/fs_mgr/tests/Android.bp
index d82d566..fdc0d8e 100644
--- a/fs_mgr/tests/Android.bp
+++ b/fs_mgr/tests/Android.bp
@@ -52,9 +52,9 @@
     ],
 }
 
-cc_prebuilt_binary {
+sh_binary_host {
     name: "adb-remount-test.sh",
-    srcs: ["adb-remount-test.sh"],
+    src: "adb-remount-test.sh",
     target: {
         darwin: {
             enabled: false,
@@ -62,11 +62,7 @@
         windows: {
             enabled: false,
         },
-        android: {
-            enabled: false,
-        },
     },
-    host_supported: true,
 }
 
 sh_test {
diff --git a/fs_mgr/tests/fs_mgr_test.cpp b/fs_mgr/tests/fs_mgr_test.cpp
index 6c881c0..e34e06e 100644
--- a/fs_mgr/tests/fs_mgr_test.cpp
+++ b/fs_mgr/tests/fs_mgr_test.cpp
@@ -1109,14 +1109,17 @@
     TemporaryFile tf;
     ASSERT_TRUE(tf.fd != -1);
     std::string fstab_contents = R"fs(
+data   /data        f2fs    noatime     wait,latemount
 system /system      erofs   ro  wait,logical,first_stage_mount
 system /system      ext4    ro  wait,logical,first_stage_mount
 vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-data   /data        f2fs    noatime     wait
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
 
+    // If GSI is installed, ReadFstabFromFile() would have called TransformFstabForDsu() implicitly.
+    // In other words, TransformFstabForDsu() would be called two times if running CTS-on-GSI,
+    // which implies TransformFstabForDsu() should be idempotent.
     Fstab fstab;
     EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
     TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
@@ -1126,10 +1129,12 @@
 
     EXPECT_EQ("/system", entry->mount_point);
     EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("erofs", entry->fs_type);
     entry++;
 
     EXPECT_EQ("/system", entry->mount_point);
     EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("ext4", entry->fs_type);
     entry++;
 
     EXPECT_EQ("/vendor", entry->mount_point);
@@ -1147,7 +1152,7 @@
     std::string fstab_contents = R"fs(
 system /system      erofs   ro  wait,logical,first_stage_mount
 vendor /vendor      ext4    ro  wait,logical,first_stage_mount
-data   /data        f2fs    noatime     wait
+data   /data        f2fs    noatime     wait,latemount
 )fs";
 
     ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
@@ -1177,3 +1182,39 @@
     EXPECT_EQ("userdata_gsi", entry->blk_device);
     entry++;
 }
+
+TEST(fs_mgr, TransformFstabForDsu_synthesisAllMissingEntries) {
+    TemporaryFile tf;
+    ASSERT_TRUE(tf.fd != -1);
+    std::string fstab_contents = R"fs(
+data   /data        f2fs    noatime     wait,latemount
+vendor /vendor      ext4    ro  wait,logical,first_stage_mount
+)fs";
+
+    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));
+
+    Fstab fstab;
+    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));
+    TransformFstabForDsu(&fstab, "dsu", {"system_gsi", "userdata_gsi"});
+    ASSERT_EQ(4U, fstab.size());
+
+    auto entry = fstab.begin();
+
+    EXPECT_EQ("/vendor", entry->mount_point);
+    EXPECT_EQ("vendor", entry->blk_device);
+    entry++;
+
+    EXPECT_EQ("/system", entry->mount_point);
+    EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("ext4", entry->fs_type);
+    entry++;
+
+    EXPECT_EQ("/system", entry->mount_point);
+    EXPECT_EQ("system_gsi", entry->blk_device);
+    EXPECT_EQ("erofs", entry->fs_type);
+    entry++;
+
+    EXPECT_EQ("/data", entry->mount_point);
+    EXPECT_EQ("userdata_gsi", entry->blk_device);
+    entry++;
+}
diff --git a/init/Android.bp b/init/Android.bp
index b4bc170..2dd9683 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -164,6 +164,7 @@
         "libcgrouprc_format",
         "libfsverity_init",
         "liblmkd_utils",
+        "liblz4",
         "libmini_keyctl_static",
         "libmodprobe",
         "libprocinfo",
@@ -362,6 +363,7 @@
         "libext2_uuid",
         "libprotobuf-cpp-lite",
         "libsnapshot_cow",
+        "liblz4",
         "libsnapshot_init",
         "update_metadata-protos",
         "libprocinfo",
diff --git a/init/TEST_MAPPING b/init/TEST_MAPPING
index fa1627c..36ca379 100644
--- a/init/TEST_MAPPING
+++ b/init/TEST_MAPPING
@@ -10,7 +10,7 @@
       "name": "MicrodroidHostTestCases"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "CtsInitTestCases"
     },
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 9e1d93c..38f6f39 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -426,7 +426,7 @@
             return ErrnoError() << "fchmodat() failed on " << options.target;
         }
     }
-    if (fscrypt_is_native()) {
+    if (IsFbeEnabled()) {
         if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {
             return reboot_into_recovery(
                     {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + options.target});
@@ -1175,7 +1175,7 @@
     auto reboot = [reboot_reason, should_reboot_into_recovery](const std::string& message) {
         // TODO (b/122850122): support this in gsi
         if (should_reboot_into_recovery) {
-            if (fscrypt_is_native() && !android::gsi::IsGsiRunning()) {
+            if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {
                 LOG(ERROR) << message << ": Rebooting into recovery, reason: " << reboot_reason;
                 if (auto result = reboot_into_recovery(
                             {"--prompt_and_wipe_data", "--reason="s + reboot_reason});
diff --git a/init/devices.cpp b/init/devices.cpp
index d4a3cb9..28406f6 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -307,8 +307,8 @@
         PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
         goto out;
     }
-    /* If the node already exists update its SELinux label to handle cases when
-     * it was created with the wrong context during coldboot procedure. */
+    /* If the node already exists update its SELinux label and the file mode to handle cases when
+     * it was created with the wrong context and file mode during coldboot procedure. */
     if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
         char* fcon = nullptr;
         int rc = lgetfilecon(path.c_str(), &fcon);
@@ -330,6 +330,11 @@
             if (gid != s.st_gid) {
                 new_group = gid;
             }
+        if (mode != s.st_mode) {
+            if (chmod(path.c_str(), mode) != 0) {
+                PLOG(ERROR) << "Cannot chmod " << path << " to " << mode;
+            }
+        }
         } else {
             PLOG(ERROR) << "Cannot stat " << path;
         }
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 042988e..4bbbc20 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -651,14 +651,9 @@
         return;
     }
 
-    std::string lp_names = "";
-    std::vector<std::string> dsu_partitions;
-    for (auto&& name : images->GetAllBackingImages()) {
-        dsu_partitions.push_back(name);
-        lp_names += name + ",";
-    }
-    // Publish the logical partition names for TransformFstabForDsu
-    WriteFile(gsi::kGsiLpNamesFile, lp_names);
+    // Publish the logical partition names for TransformFstabForDsu() and ReadFstabFromFile().
+    const auto dsu_partitions = images->GetAllBackingImages();
+    WriteFile(gsi::kGsiLpNamesFile, android::base::Join(dsu_partitions, ","));
     TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
 }
 
diff --git a/init/property_service.cpp b/init/property_service.cpp
index a1f6e04..7e92538 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1351,6 +1351,11 @@
                 InitPropertySet(persistent_property_record.name(),
                                 persistent_property_record.value());
             }
+            // Apply debug ramdisk special settings after persistent properties are loaded.
+            if (android::base::GetBoolProperty("ro.force.debuggable", false)) {
+                // Always enable usb adb if device is booted with debug ramdisk.
+                update_sys_usb_config();
+            }
             InitPropertySet("ro.persistent_properties.ready", "true");
             persistent_properties_loaded = true;
             break;
diff --git a/libcutils/TEST_MAPPING b/libcutils/TEST_MAPPING
index cca7d93..6477502 100644
--- a/libcutils/TEST_MAPPING
+++ b/libcutils/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "libcutils_test"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "libcutils_test"
     }
diff --git a/libnetutils/dhcpclient.c b/libnetutils/dhcpclient.c
index 11c116a..ad6898c 100644
--- a/libnetutils/dhcpclient.c
+++ b/libnetutils/dhcpclient.c
@@ -45,7 +45,7 @@
 
 typedef unsigned long long msecs_t;
 #if VERBOSE
-void dump_dhcp_msg();
+void dump_dhcp_msg(dhcp_msg *msg, int len);
 #endif
 
 msecs_t get_msecs(void)
diff --git a/libpackagelistparser/TEST_MAPPING b/libpackagelistparser/TEST_MAPPING
index d69a7fb..f4a7761 100644
--- a/libpackagelistparser/TEST_MAPPING
+++ b/libpackagelistparser/TEST_MAPPING
@@ -4,7 +4,7 @@
       "name": "libpackagelistparser_test"
     }
   ],
-  "hwasan-postsubmit": [
+  "hwasan-presubmit": [
     {
       "name": "libpackagelistparser_test"
     }
diff --git a/libstats/push_compat/statsd_writer.c b/libstats/push_compat/statsd_writer.c
index 04d3b46..4818d11 100644
--- a/libstats/push_compat/statsd_writer.c
+++ b/libstats/push_compat/statsd_writer.c
@@ -61,7 +61,7 @@
 static int statsdOpen();
 static void statsdClose();
 static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);
-static void statsdNoteDrop();
+static void statsdNoteDrop(int error, int tag);
 
 struct android_log_transport_write statsdLoggerWrite = {
         .name = "statsd",
diff --git a/libutils/include/utils/RefBase.h b/libutils/include/utils/RefBase.h
index e07f574..f3acd6f 100644
--- a/libutils/include/utils/RefBase.h
+++ b/libutils/include/utils/RefBase.h
@@ -779,6 +779,40 @@
 
 }  // namespace android
 
+namespace libutilsinternal {
+template <typename T, typename = void>
+struct is_complete_type : std::false_type {};
+
+template <typename T>
+struct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};
+}  // namespace libutilsinternal
+
+namespace std {
+
+// Define `RefBase` specific versions of `std::make_shared` and
+// `std::make_unique` to block people from using them. Using them to allocate
+// `RefBase` objects results in double ownership. Use
+// `sp<T>::make(...)` instead.
+//
+// Note: We exclude incomplete types because `std::is_base_of` is undefined in
+// that case.
+
+template <typename T, typename... Args,
+          typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+          typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+shared_ptr<T> make_shared(Args...) {  // SEE COMMENT ABOVE.
+    static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+template <typename T, typename... Args,
+          typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,
+          typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>
+unique_ptr<T> make_unique(Args...) {  // SEE COMMENT ABOVE.
+    static_assert(!std::is_base_of<android::RefBase, T>::value, "Must use RefBase with sp<>");
+}
+
+}  // namespace std
+
 // ---------------------------------------------------------------------------
 
 #endif // ANDROID_REF_BASE_H
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 70a3736..6d89f17 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -786,7 +786,6 @@
     mkdir /data/misc/carrierid 0770 system radio
     mkdir /data/misc/apns 0770 system radio
     mkdir /data/misc/emergencynumberdb 0770 system radio
-    mkdir /data/misc/zoneinfo 0775 system system
     mkdir /data/misc/network_watchlist 0774 system system
     mkdir /data/misc/textclassifier 0771 system system
     mkdir /data/misc/vpn 0770 system vpn
@@ -1021,10 +1020,6 @@
     # completed and apexd.status becomes "ready".
     exec_start apexd-snapshotde
 
-    # Check any timezone data in /data is newer than the copy in the time zone data
-    # module, delete if not.
-    exec - system system -- /system/bin/tzdatacheck /apex/com.android.tzdata/etc/tz /data/misc/zoneinfo
-
     # sys.memfd_use set to false by default, which keeps it disabled
     # until it is confirmed that apps and vendor processes don't make
     # IOCTLs on ashmem fds any more.
@@ -1170,6 +1165,7 @@
     chown system system /sys/kernel/ipv4/tcp_rmem_def
     chown system system /sys/kernel/ipv4/tcp_rmem_max
     chown root radio /proc/cmdline
+    chown root system /proc/bootconfig
 
     # Define default initial receive window size in segments.
     setprop net.tcp_def_init_rwnd 60