Merge "Fix soundness issue in microdroid manager." into main
diff --git a/apex/Android.bp b/apex/Android.bp
index fedcfcd..7ef2c79 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -106,6 +106,7 @@
     ],
     host_required: [
         "vm_shell",
+        "prepare_device_vfio",
     ],
     apps: [
         "EmptyPayloadApp",
@@ -136,6 +137,12 @@
     installable: false,
 }
 
+sh_binary_host {
+    name: "prepare_device_vfio",
+    src: "prepare_device_vfio.sh",
+    filename: "prepare_device_vfio.sh",
+}
+
 // Virt apex needs a custom signer for its payload
 python_binary_host {
     name: "sign_virt_apex",
diff --git a/apex/prepare_device_vfio.sh b/apex/prepare_device_vfio.sh
new file mode 100755
index 0000000..de2d502
--- /dev/null
+++ b/apex/prepare_device_vfio.sh
@@ -0,0 +1,176 @@
+#!/bin/bash
+
+# Copyright 2023 Google Inc. All rights reserved.
+#
+# 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.
+
+# prepare_device_vfio.sh: prepares a device for VFIO assignment by binding a VFIO driver to it
+
+adb="${ADB:="adb"}" # ADB command to use
+vfio_dir="/dev/vfio"
+platform_bus="/sys/bus/platform"
+vfio_reset_required="/sys/module/vfio_platform/parameters/reset_required"
+vfio_noiommu_param="/sys/module/vfio/parameters/enable_unsafe_noiommu_mode"
+vfio_unsafe_interrupts_param="/sys/module/vfio_iommu_type1/parameters/allow_unsafe_interrupts"
+
+function print_help() {
+    echo "prepare_device_vfio.sh prepares a device for VFIO assignment"
+    echo ""
+    echo " Usage:"
+    echo "    $0 DEVICE_NAME"
+    echo "      Prepare device DEVICE_NAME for VFIO assignment."
+    echo ""
+    echo "    help - prints this help message"
+}
+
+function cmd() {
+    $adb shell $@
+}
+
+function tcmd() {
+    trap "echo \"Error: adb shell command '$@' failed\" ; exit 1" ERR
+    $adb shell $@
+}
+
+function ensure_root() {
+    # Check user id
+    if [ $(cmd "id -u") != 0 ]; then
+        read -p "Must run as root; restart ADBD? [y/n] " answer
+        case $answer in
+            [Yy]* )
+                $adb root && $adb wait-for-device && sleep 3 || exit 1
+                ;;
+            * )
+                exit 1
+        esac
+    fi
+}
+
+function check_vfio() {
+    cmd "[ -c $vfio_dir/vfio ]"
+    if [ $? -ne 0 ]; then
+        echo "cannot find $vfio_dir/vfio"
+        exit 1
+    fi
+
+    cmd "[ -d $platform_bus/drivers/vfio-platform ]"
+    if [ $? -ne 0 ]; then
+        echo "VFIO-platform is not supported"
+        exit 1
+    fi
+}
+
+function check_device() {
+    cmd "[ -d $device_sys ]"
+    if [ $? -ne 0 ]; then
+        echo "no device $device ($device_sys)"
+        exit 1
+    fi
+}
+
+function get_device_iommu_group() {
+    local group=$(cmd "basename \$(readlink \"$device_sys/iommu_group\")")
+    if [ $? -eq 0 ]; then
+        echo $group
+    else
+        echo ""
+    fi
+}
+
+function misc_setup() {
+    # VFIO NOIOMMU check
+    if [ -z "$group" ]; then
+        echo "$device_sys does not have an IOMMU group - setting $vfio_noiommu_param"
+        tcmd "echo y > \"$vfio_noiommu_param\""
+    fi
+
+    # Disable SELinux to allow virtualizationmanager and crosvm to access sysfs
+    echo "[*WARN*] setenforce=0: SELinux is disabled"
+    tcmd "setenforce 0"
+
+    # Samsung IOMMU does not report interrupt remapping support, so enable unsafe uinterrupts
+    if [ -n "$group" ]; then
+        local iommu_drv=$(cmd "basename \$(readlink \"$device_sys/iommu/device/driver\")")
+        if [ "$iommu_drv" = "samsung-sysmmu-v9" ]; then
+            tcmd "echo y > \"$vfio_unsafe_interrupts_param\""
+        fi
+    fi
+}
+
+function bind_vfio_driver() {
+    # Check if non-VFIO driver is currently bound, ie unbinding is needed
+    cmd "[ -e \"$device_driver\" ] && \
+        [ ! \$(basename \$(readlink \"$device_driver\")) = \"vfio-platform\" ]"
+            if [ $? -eq 0 ]; then
+                # Unbind current driver
+                tcmd "echo \"$device\" > \"$device_driver/unbind\""
+            fi
+
+    # Bind to VFIO driver
+    cmd "[ ! -e \"$device_driver\" ]"
+    if [ $? -eq 0 ]; then
+        # Bind vfio-platform driver
+        tcmd "echo \"vfio-platform\" > \"$device_sys/driver_override\""
+        tcmd "echo \"$device\" > \"$platform_bus/drivers_probe\""
+        sleep 2
+    fi
+}
+
+function verify_vfio_driver() {
+    # Verify new VFIO file structure
+    group=$(get_device_iommu_group)
+    if [ -z "$group" ]; then
+        echo "cannot setup VFIO-NOIOMMU for $device_sys"
+        exit 1
+    fi
+
+    cmd "[ ! -c \"$vfio_dir/$group\" ] || \
+        [ ! -e \"$device_driver\" ] || \
+        [ ! \$(basename \$(readlink \"$device_driver\")) = \"vfio-platform\" ]"
+    if [ $? -eq 0 ]; then
+        echo "could not bind $device to VFIO platform driver"
+
+        if [ $(cmd "cat $vfio_reset_required") = Y ]; then
+            echo "VFIO device reset handler must be registered. Either unset $vfio_reset_required, \
+or register a reset handler for $device_sys"
+        fi
+        exit 1
+    fi
+}
+
+function prepare_device() {
+    device="$1"
+    device_sys="/sys/bus/platform/devices/$device"
+    device_driver="$device_sys/driver"
+
+    ensure_root
+    check_vfio
+    check_device
+    group=$(get_device_iommu_group)
+    misc_setup
+
+    bind_vfio_driver
+    verify_vfio_driver
+
+    echo "Device: $device_sys"
+    echo "IOMMU group: $group"
+    echo "VFIO group file: $vfio_dir/$group"
+    echo "Ready!"
+}
+
+cmd=$1
+
+case $cmd in
+    ""|help) print_help ;;
+    *) prepare_device "$cmd" $@ ;;
+esac
diff --git a/apkdmverity/Android.bp b/apkdmverity/Android.bp
index fae7e99..8429263 100644
--- a/apkdmverity/Android.bp
+++ b/apkdmverity/Android.bp
@@ -40,7 +40,7 @@
     name: "apkdmverity.test",
     defaults: [
         "apkdmverity.defaults",
-        "ignorabletest.defaults",
+        "rdroidtest.defaults",
     ],
     test_suites: ["general-tests"],
     compile_multilib: "first",
diff --git a/apkdmverity/src/main.rs b/apkdmverity/src/main.rs
index 55953a9..d9e9e2b 100644
--- a/apkdmverity/src/main.rs
+++ b/apkdmverity/src/main.rs
@@ -153,12 +153,12 @@
 }
 
 #[cfg(test)]
-ignorabletest::test_main!();
+rdroidtest::test_main!();
 
 #[cfg(test)]
 mod tests {
     use crate::*;
-    use ignorabletest::test;
+    use rdroidtest::test;
     use std::fs::{File, OpenOptions};
     use std::io::Write;
     use std::ops::Deref;
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 48f7dcb..64b340a 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -386,7 +386,8 @@
 }
 
 cfg_if::cfg_if! {
-    if #[cfg(all(target_arch = "aarch64", target_pointer_width = "64"))] {
+    if #[cfg(all(any(target_arch = "aarch64", target_arch = "riscv64"),
+                 target_pointer_width = "64"))] {
         fn blk_size() -> libc::c_int { CHUNK_SIZE as libc::c_int }
     } else {
         fn blk_size() -> libc::c_long { CHUNK_SIZE as libc::c_long }
diff --git a/authfs/tests/benchmarks/Android.bp b/authfs/tests/benchmarks/Android.bp
index 110d000..cea5a81 100644
--- a/authfs/tests/benchmarks/Android.bp
+++ b/authfs/tests/benchmarks/Android.bp
@@ -17,12 +17,10 @@
     test_suites: ["general-tests"],
     data_device_bins_first: [
         "open_then_run",
-        "fsverity",
     ],
     per_testcase_directory: true,
     data: [
         ":authfs_test_files",
-        ":CtsApkVerityTestPrebuiltFiles",
         ":MicrodroidTestApp",
     ],
     required: ["MicrodroidTestPreparer"],
diff --git a/authfs/tests/benchmarks/AndroidTest.xml b/authfs/tests/benchmarks/AndroidTest.xml
index 9216006..715f352 100644
--- a/authfs/tests/benchmarks/AndroidTest.xml
+++ b/authfs/tests/benchmarks/AndroidTest.xml
@@ -34,32 +34,16 @@
 
         <!-- Test executable -->
         <option name="push-file" key="open_then_run" value="/data/local/tmp/open_then_run" />
-        <option name="push-file" key="fsverity" value="/data/local/tmp/fsverity" />
 
         <!-- Test data files -->
         <option name="push-file" key="cert.der" value="/data/local/tmp/authfs/cert.der" />
         <option name="push-file" key="input.4m" value="/data/local/tmp/authfs/input.4m" />
         <option name="push-file" key="input.4m.fsv_meta"
             value="/data/local/tmp/authfs/input.4m.fsv_meta" />
-
-        <!-- Just pick a file with signature that can be trused on the device. -->
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk"
-            value="/data/local/tmp/authfs/input.apk" />
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk.fsv_sig"
-            value="/data/local/tmp/authfs/input.apk.fsv_sig" />
     </target_preparer>
 
     <target_preparer class="com.android.microdroid.test.preparer.DisableMicrodroidDebugPolicyPreparer" />
 
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="throw-if-cmd-fail" value="true" />
-        <!-- Now that the files are pushed to the device, enable fs-verity for the targeting file.
-             It works because the signature is trusted on all CTS compatible devices. -->
-        <option name="run-command"
-            value="cd /data/local/tmp/authfs;
-                   ../fsverity enable input.apk --signature=input.apk.fsv_sig" />
-    </target_preparer>
-
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="AuthFsBenchmarks.jar" />
     </test>
diff --git a/authfs/tests/hosttests/Android.bp b/authfs/tests/hosttests/Android.bp
index 4b8151d..83ef853 100644
--- a/authfs/tests/hosttests/Android.bp
+++ b/authfs/tests/hosttests/Android.bp
@@ -20,7 +20,6 @@
     per_testcase_directory: true,
     data: [
         ":authfs_test_files",
-        ":CtsApkVerityTestPrebuiltFiles",
         ":MicrodroidTestApp",
     ],
 }
diff --git a/authfs/tests/hosttests/AndroidTest.xml b/authfs/tests/hosttests/AndroidTest.xml
index 2ccc45f..5920630 100644
--- a/authfs/tests/hosttests/AndroidTest.xml
+++ b/authfs/tests/hosttests/AndroidTest.xml
@@ -50,18 +50,13 @@
         <option name="push-file" key="input.4m.fsv_meta.bad_merkle"
             value="/data/local/tmp/authfs/input.4m.fsv_meta.bad_merkle" />
 
-        <!-- Just pick a file with signature that can be trused on the device. -->
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk"
-            value="/data/local/tmp/authfs/input.apk" />
-        <option name="push-file" key="CtsApkVerityTestAppPrebuilt.apk.fsv_sig"
-            value="/data/local/tmp/authfs/input.apk.fsv_sig" />
+        <option name="push-file" key="input.4m" value="/data/local/tmp/authfs/input.file" />
     </target_preparer>
 
     <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
         <option name="throw-if-cmd-fail" value="true" />
-        <!-- Now that the files are pushed to the device, enable fs-verity for the targeting file.
-             It works because the signature is trusted on all CTS compatible devices. -->
-        <option name="run-command" value="cd /data/local/tmp/authfs; ../fsverity enable input.apk --signature=input.apk.fsv_sig" />
+        <!-- Now that the files are pushed to the device, enable fs-verity for the targeting file. -->
+        <option name="run-command" value="cd /data/local/tmp/authfs; ../fsverity enable input.file" />
     </target_preparer>
 
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
diff --git a/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java b/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
index 440f5ca..d0a7c66 100644
--- a/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
+++ b/authfs/tests/hosttests/java/src/com/android/fs/AuthFsHostTest.java
@@ -145,17 +145,17 @@
 
     @Test
     public void testReadWithFsverityVerification_FdServerUsesRealFsverityData() throws Exception {
-        // Setup (fs-verity is enabled for input.apk in AndroidTest.xml)
-        runFdServerOnAndroid("--open-ro 3:input.apk", "--ro-fds 3");
-        String expectedDigest = sAndroid.run(
-                FSVERITY_BIN + " digest --compact " + TEST_DIR + "/input.apk");
+        // Setup (fs-verity is enabled for input.file in AndroidTest.xml)
+        runFdServerOnAndroid("--open-ro 3:input.file", "--ro-fds 3");
+        String expectedDigest =
+                sAndroid.run(FSVERITY_BIN + " digest --compact " + TEST_DIR + "/input.file");
         runAuthFsOnMicrodroid("--remote-ro-file 3:sha256-" + expectedDigest);
 
         // Action
         String actualHash = computeFileHash(sMicrodroid, MOUNT_DIR + "/3");
 
         // Verify
-        String expectedHash = computeFileHash(sAndroid, TEST_DIR + "/input.apk");
+        String expectedHash = computeFileHash(sAndroid, TEST_DIR + "/input.file");
         assertEquals("Inconsistent hash from /authfs/3: ", expectedHash, actualHash);
     }
 
diff --git a/libs/devicemapper/Android.bp b/libs/devicemapper/Android.bp
index b7cdedc..29f2f5f 100644
--- a/libs/devicemapper/Android.bp
+++ b/libs/devicemapper/Android.bp
@@ -33,7 +33,7 @@
     name: "libdm_rust.test",
     defaults: [
         "libdm_rust.defaults",
-        "ignorabletest.defaults",
+        "rdroidtest.defaults",
     ],
     test_suites: ["general-tests"],
     rustlibs: [
diff --git a/libs/devicemapper/src/lib.rs b/libs/devicemapper/src/lib.rs
index 0170795..868ac5a 100644
--- a/libs/devicemapper/src/lib.rs
+++ b/libs/devicemapper/src/lib.rs
@@ -233,13 +233,13 @@
 }
 
 #[cfg(test)]
-ignorabletest::test_main!();
+rdroidtest::test_main!();
 
 #[cfg(test)]
 mod tests {
     use super::*;
     use crypt::{CipherType, DmCryptTargetBuilder};
-    use ignorabletest::test;
+    use rdroidtest::test;
     use rustutils::system_properties;
     use std::fs::{read, File, OpenOptions};
     use std::io::Write;
diff --git a/libs/ignorabletest/Android.bp b/libs/ignorabletest/Android.bp
deleted file mode 100644
index 4ae89af..0000000
--- a/libs/ignorabletest/Android.bp
+++ /dev/null
@@ -1,36 +0,0 @@
-rust_library {
-    name: "libignorabletest",
-    host_supported: true,
-    crate_name: "ignorabletest",
-    cargo_env_compat: true,
-    cargo_pkg_version: "0.1.0",
-    srcs: ["src/lib.rs"],
-    edition: "2021",
-    rustlibs: [
-        "liblibtest_mimic",
-        "liblinkme",
-        "liblog_rust",
-        "liblogger",
-    ],
-    proc_macros: ["libpaste"],
-    apex_available: [
-        "//apex_available:platform",
-        "//apex_available:anyapex",
-    ],
-}
-
-rust_defaults {
-    name: "ignorabletest.defaults",
-    test_harness: false,
-    cfgs: ["test"],
-    rustlibs: [
-        "libignorabletest",
-        "liblinkme",
-    ],
-    // Without this flag we get linker errors saying to add it. See
-    // https://github.com/dtolnay/linkme/issues/49 and related issues.
-    ld_flags: [
-        "-z",
-        "nostart-stop-gc",
-    ],
-}
diff --git a/libs/ignorabletest/README.md b/libs/ignorabletest/README.md
deleted file mode 100644
index 77140bd..0000000
--- a/libs/ignorabletest/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# ignorabletest
-
-This is a custom Rust test harness which allows tests to be ignored at runtime based on arbitrary
-criteria. The built-in Rust test harness only allows tests to be ignored at compile time, but this
-is often not enough on Android, where we want to ignore tests based on system properties or other
-characteristics of the device on which the test is being run, which are not known at build time.
-
-## Usage
-
-Unfortunately without the built-in support that rustc provides to the standard test harness, this
-one is slightly more cumbersome to use. Firstly, add it to the `rust_test` build rule in your
-`Android.bp` by adding the defaults provided:
-
-```soong
-rust_test {
-    name: "mycrate.test",
-    defaults: ["ignorabletest.defaults"],
-    // ...
-}
-```
-
-If you are testing a binary that has a `main` function, you'll need to remove it from the test
-build:
-
-```rust
-#[cfg(not(test))]
-fn main() {
-    // ...
-}
-```
-
-(If you're testing a library or anything else which doesn't have a `main` function, you don't need
-to worry about this.)
-
-Each test case should be marked with the `ignorabletest::test!` macro, rather than the standard
-`#[test]` attribute:
-
-```rust
-use ignorabletest::test;
-
-test!(one_plus_one);
-fn one_plus_one {
-    assert_eq!(1 + 1, 2);
-}
-```
-
-To ignore a test, you can add an `ignore_if` clause with a boolean expression:
-
-```rust
-use ignorabletest::test;
-
-test!(clap_hands, ignore_if: !feeling_happy());
-fn clap_hands {
-    assert!(HANDS.clap().is_ok());
-}
-```
-
-Somewhere in your main module, you need to use the `test_main` macro to generate an entry point for
-the test harness:
-
-```rust
-#[cfg(test)]
-ignorabletest::test_main!();
-```
-
-You can then run your tests as usual with `atest`.
diff --git a/libs/ignorabletest/src/lib.rs b/libs/ignorabletest/src/lib.rs
deleted file mode 100644
index c7243e6..0000000
--- a/libs/ignorabletest/src/lib.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-//! Test harness which supports ignoring tests at runtime.
-
-pub mod runner;
-
-#[doc(hidden)]
-pub use libtest_mimic as _libtest_mimic;
-#[doc(hidden)]
-pub use linkme as _linkme;
-#[doc(hidden)]
-pub use paste as _paste;
-
-/// Macro to generate the main function for the test harness.
-#[macro_export]
-macro_rules! test_main {
-    () => {
-        #[cfg(test)]
-        fn main() {
-            ignorabletest::runner::main()
-        }
-    };
-}
-
-/// Macro to generate a wrapper function for a single test.
-///
-/// # Usage
-///
-/// ```
-/// test!(test_string_equality);
-/// fn test_string_equality() {
-///   assert_eq!("", "");
-/// }
-/// ```
-#[macro_export]
-macro_rules! test {
-    ($test_name:ident) => {
-        $crate::_paste::paste!(
-            #[$crate::_linkme::distributed_slice($crate::runner::IGNORABLETEST_TESTS)]
-            fn [< __test_ $test_name >]() -> $crate::_libtest_mimic::Trial {
-                $crate::_libtest_mimic::Trial::test(
-                    ::std::stringify!($test_name),
-                    move || ignorabletest::runner::run($test_name),
-                )
-            }
-        );
-    };
-    ($test_name:ident, ignore_if: $ignore_expr:expr) => {
-        $crate::_paste::paste!(
-            #[$crate::_linkme::distributed_slice($crate::runner::IGNORABLETEST_TESTS)]
-            fn [< __test_ $test_name >]() -> $crate::_libtest_mimic::Trial {
-                $crate::_libtest_mimic::Trial::test(
-                    ::std::stringify!($test_name),
-                    move || ignorabletest::runner::run($test_name),
-                ).with_ignored_flag($ignore_expr)
-            }
-        );
-    };
-}
diff --git a/libs/ignorabletest/src/runner.rs b/libs/ignorabletest/src/runner.rs
deleted file mode 100644
index fdac406..0000000
--- a/libs/ignorabletest/src/runner.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! Test runner.
-
-use core::ops::{Deref, FnOnce};
-use libtest_mimic::{Arguments, Failed, Trial};
-use linkme::distributed_slice;
-use log::Level;
-use std::env;
-
-/// Command-line arguments to ignore, because they are not supported by libtest-mimic.
-const IGNORED_ARGS: [&str; 2] = ["-Zunstable-options", "--report-time"];
-
-/// The collection of all tests to run.
-#[doc(hidden)]
-#[distributed_slice]
-pub static IGNORABLETEST_TESTS: [fn() -> Trial] = [..];
-
-/// Runs all tests.
-pub fn main() {
-    logger::init(logger::Config::default().with_min_level(Level::Debug));
-    let args = Arguments::from_iter(env::args().filter(|arg| !IGNORED_ARGS.contains(&arg.deref())));
-    let tests = IGNORABLETEST_TESTS.iter().map(|test| test()).collect();
-    libtest_mimic::run(&args, tests).exit();
-}
-
-/// Runs the given test.
-pub fn run(test: impl FnOnce()) -> Result<(), Failed> {
-    test();
-    Ok(())
-}
diff --git a/microdroid/kdump/Android.bp b/microdroid/kdump/Android.bp
index cc681a7..b9a18fe 100644
--- a/microdroid/kdump/Android.bp
+++ b/microdroid/kdump/Android.bp
@@ -18,6 +18,9 @@
     static_executable: true,
     installable: false,
     compile_multilib: "64",
+    sanitize: {
+        hwaddress: false, // HWASAN setup fails when run as init process
+    },
 }
 
 android_filesystem {
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
index 23f8ca6..2ea748b 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
@@ -28,10 +28,12 @@
 
     private static final String KEY_VENDOR_DEVICE = "ro.product.vendor.device";
     private static final String KEY_BUILD_TYPE = "ro.build.type";
+    private static final String KEY_PRODUCT_NAME = "ro.product.name";
     private static final String KEY_METRICS_TAG = "debug.hypervisor.metrics_tag";
 
     private static final String CUTTLEFISH_DEVICE_PREFIX = "vsoc_";
     private static final String USER_BUILD_TYPE = "user";
+    private static final String HWASAN_SUFFIX = "_hwasan";
 
     private final PropertyGetter mPropertyGetter;
 
@@ -53,6 +55,14 @@
     }
 
     /**
+     * @return whether the build is HWASAN.
+     */
+    public boolean isHwasan() {
+        String productName = getProperty(KEY_PRODUCT_NAME);
+        return productName != null && productName.contains(HWASAN_SUFFIX);
+    }
+
+    /**
      * @return whether the device is user build.
      */
     public boolean isUserBuild() {
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 81ccec7..98327a9 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -89,6 +89,10 @@
         return DeviceProperties.create(getDevice()::getProperty).isCuttlefish();
     }
 
+    protected boolean isHwasan() {
+        return DeviceProperties.create(getDevice()::getProperty).isHwasan();
+    }
+
     protected String getMetricPrefix() {
         return MetricsProcessor.getMetricPrefix(
                 DeviceProperties.create(getDevice()::getProperty).getMetricsTag());
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 092325e..82d8571 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -523,12 +523,16 @@
     @Test
     public void testTombstonesAreGeneratedUponUserspaceCrashOnNonPvm() throws Exception {
         assumeNonProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         testTombstonesAreGeneratedUponUserspaceCrash(false);
     }
 
     @Test
     public void testTombstonesAreGeneratedUponUserspaceCrashOnPvm() throws Exception {
         assumeProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         testTombstonesAreGeneratedUponUserspaceCrash(true);
     }
 
@@ -548,6 +552,8 @@
     public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrashOnNonPvm()
             throws Exception {
         assumeNonProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(false);
     }
 
@@ -555,6 +561,8 @@
     public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrashOnPvm()
             throws Exception {
         assumeProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash(true);
     }
 
@@ -636,6 +644,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashPayloadOnPvm() throws Exception {
         assumeProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
                         isTombstoneGeneratedWithCrashPayload(
                                 /*protectedVm=*/ true, /*debuggable=*/ true))
@@ -645,6 +655,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashPayloadOnNonPvm() throws Exception {
         assumeNonProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
                         isTombstoneGeneratedWithCrashPayload(
                                 /*protectedVm=*/ false, /*debuggable=*/ true))
@@ -655,6 +667,8 @@
     public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggableOnPvm()
             throws Exception {
         assumeProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
                         isTombstoneGeneratedWithCrashPayload(
                                 /*protectedVm=*/ true, /*debuggable=*/ false))
@@ -665,6 +679,8 @@
     public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggableOnNonPvm()
             throws Exception {
         assumeNonProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
                         isTombstoneGeneratedWithCrashPayload(
                                 /*protectedVm=*/ false, /*debuggable=*/ false))
@@ -680,6 +696,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashConfigOnPvm() throws Exception {
         assumeProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(isTombstoneGeneratedWithCrashConfig(/*protectedVm=*/ true, /*debuggable=*/ true))
                 .isTrue();
     }
@@ -687,6 +705,8 @@
     @Test
     public void testTombstonesAreGeneratedWithCrashConfigOnNonPvm() throws Exception {
         assumeNonProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
                         isTombstoneGeneratedWithCrashConfig(
                                 /*protectedVm=*/ false, /*debuggable=*/ true))
@@ -697,6 +717,8 @@
     public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggableOnPvm()
             throws Exception {
         assumeProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
                         isTombstoneGeneratedWithCrashConfig(
                                 /*protectedVm=*/ true, /*debuggable=*/ false))
@@ -707,6 +729,8 @@
     public void testTombstonesAreNotGeneratedWithCrashConfigWhenNonDebuggableOnNonPvm()
             throws Exception {
         assumeNonProtectedVmSupported();
+        // TODO(b/291867858): tombstones are failing in HWASAN enabled Microdroid.
+        assumeFalse("tombstones are failing in HWASAN enabled Microdroid.", isHwasan());
         assertThat(
                         isTombstoneGeneratedWithCrashConfig(
                                 /*protectedVm=*/ false, /*debuggable=*/ false))
diff --git a/virtualizationmanager/src/payload.rs b/virtualizationmanager/src/payload.rs
index ab6f31c..343f3cf 100644
--- a/virtualizationmanager/src/payload.rs
+++ b/virtualizationmanager/src/payload.rs
@@ -81,6 +81,9 @@
 
     #[serde(rename = "provideSharedApexLibs")]
     provide_shared_apex_libs: bool,
+
+    #[serde(rename = "preinstalledModulePath")]
+    preinstalled_path: PathBuf,
 }
 
 impl ApexInfoList {
@@ -275,7 +278,7 @@
     let apex_list = pm.get_apex_list(vm_payload_config.prefer_staged)?;
 
     // collect APEXes from config
-    let mut apex_infos = collect_apex_infos(&apex_list, &vm_payload_config.apexes, debug_config);
+    let mut apex_infos = collect_apex_infos(&apex_list, &vm_payload_config.apexes, debug_config)?;
 
     // Pass sorted list of apexes. Sorting key shouldn't use `path` because it will change after
     // reboot with prefer_staged. `last_update_seconds` is added to distinguish "samegrade"
@@ -376,18 +379,28 @@
     Ok(apexes)
 }
 
+fn check_apexes_are_from_allowed_partitions(requested_apexes: &Vec<&ApexInfo>) -> Result<()> {
+    const ALLOWED_PARTITIONS: [&str; 2] = ["/system", "/system_ext"];
+    for apex in requested_apexes {
+        if !ALLOWED_PARTITIONS.iter().any(|p| apex.preinstalled_path.starts_with(p)) {
+            bail!("Non-system APEX {} is not supported in Microdroid", apex.name);
+        }
+    }
+    Ok(())
+}
+
 // Collect ApexInfos from VM config
 fn collect_apex_infos<'a>(
     apex_list: &'a ApexInfoList,
     apex_configs: &[ApexConfig],
     debug_config: &DebugConfig,
-) -> Vec<&'a ApexInfo> {
+) -> Result<Vec<&'a ApexInfo>> {
     let mut additional_apexes: Vec<&str> = MICRODROID_REQUIRED_APEXES.to_vec();
     if debug_config.should_include_debug_apexes() {
         additional_apexes.extend(MICRODROID_REQUIRED_APEXES_DEBUG.to_vec());
     }
 
-    apex_list
+    let apex_infos = apex_list
         .list
         .iter()
         .filter(|ai| {
@@ -395,7 +408,10 @@
                 || additional_apexes.iter().any(|name| name == &ai.name && ai.is_active)
                 || ai.provide_shared_apex_libs
         })
-        .collect()
+        .collect();
+
+    check_apexes_are_from_allowed_partitions(&apex_infos)?;
+    Ok(apex_infos)
 }
 
 pub fn add_microdroid_vendor_image(vendor_image: File, vm_config: &mut VirtualMachineRawConfig) {
@@ -488,13 +504,14 @@
     }
 
     #[test]
-    fn test_collect_apexes() {
+    fn test_collect_apexes() -> Result<()> {
         let apex_info_list = ApexInfoList {
             list: vec![
                 ApexInfo {
                     // 0
                     name: "com.android.adbd".to_string(),
                     path: PathBuf::from("adbd"),
+                    preinstalled_path: PathBuf::from("/system/adbd"),
                     has_classpath_jar: false,
                     last_update_seconds: 12345678,
                     is_factory: true,
@@ -505,6 +522,7 @@
                     // 1
                     name: "com.android.os.statsd".to_string(),
                     path: PathBuf::from("statsd"),
+                    preinstalled_path: PathBuf::from("/system/statsd"),
                     has_classpath_jar: false,
                     last_update_seconds: 12345678,
                     is_factory: true,
@@ -515,6 +533,7 @@
                     // 2
                     name: "com.android.os.statsd".to_string(),
                     path: PathBuf::from("statsd/updated"),
+                    preinstalled_path: PathBuf::from("/system/statsd"),
                     has_classpath_jar: false,
                     last_update_seconds: 12345678 + 1,
                     is_factory: false,
@@ -545,6 +564,7 @@
                     // 5
                     name: "has_classpath".to_string(),
                     path: PathBuf::from("has_classpath/updated"),
+                    preinstalled_path: PathBuf::from("/system/has_classpath"),
                     has_classpath_jar: true,
                     last_update_seconds: 87654321 + 1,
                     is_factory: false,
@@ -555,6 +575,7 @@
                     // 6
                     name: "apex-foo".to_string(),
                     path: PathBuf::from("apex-foo"),
+                    preinstalled_path: PathBuf::from("/system/apex-foo"),
                     has_classpath_jar: false,
                     last_update_seconds: 87654321,
                     is_factory: true,
@@ -565,6 +586,7 @@
                     // 7
                     name: "apex-foo".to_string(),
                     path: PathBuf::from("apex-foo/updated"),
+                    preinstalled_path: PathBuf::from("/system/apex-foo"),
                     has_classpath_jar: false,
                     last_update_seconds: 87654321 + 1,
                     is_factory: false,
@@ -575,6 +597,7 @@
                     // 8
                     name: "sharedlibs".to_string(),
                     path: PathBuf::from("apex-foo"),
+                    preinstalled_path: PathBuf::from("/system/apex-foo"),
                     last_update_seconds: 87654321,
                     is_factory: true,
                     provide_shared_apex_libs: true,
@@ -584,6 +607,7 @@
                     // 9
                     name: "sharedlibs".to_string(),
                     path: PathBuf::from("apex-foo/updated"),
+                    preinstalled_path: PathBuf::from("/system/apex-foo"),
                     last_update_seconds: 87654321 + 1,
                     is_active: true,
                     provide_shared_apex_libs: true,
@@ -596,7 +620,11 @@
             ApexConfig { name: "{CLASSPATH}".to_string() },
         ];
         assert_eq!(
-            collect_apex_infos(&apex_info_list, &apex_configs, &DebugConfig::new(DebugLevel::FULL)),
+            collect_apex_infos(
+                &apex_info_list,
+                &apex_configs,
+                &DebugConfig::new(DebugLevel::FULL)
+            )?,
             vec![
                 // Pass active/required APEXes
                 &apex_info_list.list[0],
@@ -609,6 +637,55 @@
                 &apex_info_list.list[9],
             ]
         );
+        Ok(())
+    }
+
+    #[test]
+    fn test_check_allowed_partitions_vendor_not_allowed() -> Result<()> {
+        let apex_info_list = ApexInfoList {
+            list: vec![ApexInfo {
+                name: "apex-vendor".to_string(),
+                path: PathBuf::from("apex-vendor"),
+                preinstalled_path: PathBuf::from("/vendor/apex-vendor"),
+                is_active: true,
+                ..Default::default()
+            }],
+        };
+        let apex_configs = vec![ApexConfig { name: "apex-vendor".to_string() }];
+
+        let ret =
+            collect_apex_infos(&apex_info_list, &apex_configs, &DebugConfig::new(DebugLevel::NONE));
+        assert!(ret
+            .is_err_and(|ret| ret.to_string()
+                == "Non-system APEX apex-vendor is not supported in Microdroid"));
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_check_allowed_partitions_system_ext_allowed() -> Result<()> {
+        let apex_info_list = ApexInfoList {
+            list: vec![ApexInfo {
+                name: "apex-system_ext".to_string(),
+                path: PathBuf::from("apex-system_ext"),
+                preinstalled_path: PathBuf::from("/system_ext/apex-system_ext"),
+                is_active: true,
+                ..Default::default()
+            }],
+        };
+
+        let apex_configs = vec![ApexConfig { name: "apex-system_ext".to_string() }];
+
+        assert_eq!(
+            collect_apex_infos(
+                &apex_info_list,
+                &apex_configs,
+                &DebugConfig::new(DebugLevel::NONE)
+            )?,
+            vec![&apex_info_list.list[0]]
+        );
+
+        Ok(())
     }
 
     #[test]
diff --git a/vmbase/src/memory/shared.rs b/vmbase/src/memory/shared.rs
index 68f9ebd..dfa29e4 100644
--- a/vmbase/src/memory/shared.rs
+++ b/vmbase/src/memory/shared.rs
@@ -77,10 +77,6 @@
     payload_range: Option<MemoryRange>,
 }
 
-// TODO: Remove this once aarch64-paging crate is updated.
-// SAFETY: Only `PageTable` doesn't implement Send, but it should.
-unsafe impl Send for MemoryTracker {}
-
 impl MemoryTracker {
     const CAPACITY: usize = 5;
     const MMIO_CAPACITY: usize = 5;
@@ -355,17 +351,16 @@
 }
 
 fn try_shared_alloc(layout: Layout) -> Option<NonNull<u8>> {
-    // Adjusts the layout size to the max of the next power of two and the alignment,
-    // as this is the actual size of the memory allocated in `alloc_aligned()`.
-    let size = max(layout.size().next_power_of_two(), layout.align());
-    let layout = Layout::from_size_align(size, layout.align()).unwrap();
-
     let mut shared_pool = SHARED_POOL.get().unwrap().lock();
 
     if let Some(buffer) = shared_pool.alloc_aligned(layout) {
         Some(NonNull::new(buffer as _).unwrap())
     } else if let Some(shared_memory) = SHARED_MEMORY.lock().as_mut() {
-        shared_memory.refill(&mut shared_pool, layout);
+        // Adjusts the layout size to the max of the next power of two and the alignment,
+        // as this is the actual size of the memory allocated in `alloc_aligned()`.
+        let size = max(layout.size().next_power_of_two(), layout.align());
+        let refill_layout = Layout::from_size_align(size, layout.align()).unwrap();
+        shared_memory.refill(&mut shared_pool, refill_layout);
         shared_pool.alloc_aligned(layout).map(|buffer| NonNull::new(buffer as _).unwrap())
     } else {
         None