Fix mte tests to allow auto-upgrade.

The kernel MTE mode auto-upgrade feature (see mte_tcf_preferred) allows
silent auto-upgrade of Async MTE mode to Asymm or Sync depending on the
OS settings. Relax the tests to allow either Sync or Async faults when
requesting Async mode, and only Sync faults when requesting Sync.

Also tighten the MTENote test to check that both system allocator and
prctl are off when MTE is disabled.

Bug: 214622342
Test: bionic-unit-tests on fvp with all variations of
    /sys/devices/system/cpu/cpu*/mte_tcf_preferred=(async|asymm|sync)

Change-Id: I77a92789d6b330742d00b08c9f0fecc3a6c8fca6
diff --git a/tests/heap_tagging_level_test.cpp b/tests/heap_tagging_level_test.cpp
index cfb2490..c493e1d 100644
--- a/tests/heap_tagging_level_test.cpp
+++ b/tests/heap_tagging_level_test.cpp
@@ -81,10 +81,20 @@
 #endif // defined(__BIONIC__)
 }
 
+namespace {
 #if defined(__BIONIC__) && defined(__aarch64__)
 void ExitWithSiCode(int, siginfo_t* info, void*) {
   _exit(info->si_code);
 }
+
+template <typename Pred>
+class Or {
+  Pred A, B;
+
+ public:
+  Or(Pred A, Pred B) : A(A), B(B) {}
+  bool operator()(int exit_status) { return A(exit_status) || B(exit_status); }
+};
 #endif
 
 TEST(heap_tagging_level, sync_async_bad_accesses_die) {
@@ -94,6 +104,7 @@
   }
 
   std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
+  volatile int sink ATTRIBUTE_UNUSED;
 
   // We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a
   // mismatching tag before each allocation.
@@ -104,6 +115,12 @@
         p[-1] = 42;
       },
       testing::ExitedWithCode(SEGV_MTESERR), "");
+  EXPECT_EXIT(
+      {
+        ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
+        sink = p[-1];
+      },
+      testing::ExitedWithCode(SEGV_MTESERR), "");
 
   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
   EXPECT_EXIT(
@@ -111,14 +128,21 @@
         ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
         p[-1] = 42;
       },
-      testing::ExitedWithCode(SEGV_MTEAERR), "");
+      Or(testing::ExitedWithCode(SEGV_MTESERR), testing::ExitedWithCode(SEGV_MTEAERR)), "");
+  EXPECT_EXIT(
+      {
+        ScopedSignalHandler ssh(SIGSEGV, ExitWithSiCode, SA_SIGINFO);
+        sink = p[-1];
+      },
+      Or(testing::ExitedWithCode(SEGV_MTESERR), testing::ExitedWithCode(SEGV_MTEAERR)), "");
 
   EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
-  volatile int oob ATTRIBUTE_UNUSED = p[-1];
+  sink = p[-1];
 #else
   GTEST_SKIP() << "bionic/arm64 only";
 #endif
 }
+}  // namespace
 
 TEST(heap_tagging_level, none_pointers_untagged) {
 #if defined(__BIONIC__)
@@ -205,7 +229,9 @@
   const char* kNoteSuffix[] = {"disabled", "async", "sync"};
   const char* kExpectedOutputHWASAN[] = {".*tag-mismatch.*", ".*tag-mismatch.*",
                                          ".*tag-mismatch.*"};
-  const char* kExpectedOutputMTE[] = {"normal exit\n", "SEGV_MTEAERR\n", "SEGV_MTESERR\n"};
+  // Note that we do not check the exact si_code of the "async" variant, as it may be auto-upgraded
+  // to asymm or even sync.
+  const char* kExpectedOutputMTE[] = {"normal exit\n", "SEGV_MTE[AS]ERR\n", "SEGV_MTESERR\n"};
   const char* kExpectedOutputNonMTE[] = {"normal exit\n", "normal exit\n", "normal exit\n"};
   const char** kExpectedOutput =
       withHWASAN ? kExpectedOutputHWASAN : (withMTE ? kExpectedOutputMTE : kExpectedOutputNonMTE);
@@ -215,7 +241,6 @@
   bool isStatic = std::get<1>(GetParam());
   std::string helper_base = std::string("heap_tagging_") + (isStatic ? "static_" : "") +
                             kNoteSuffix[static_cast<int>(note)] + "_helper";
-  fprintf(stderr, "=== %s\n", helper_base.c_str());
   std::string helper = GetTestlibRoot() + "/" + helper_base;
   chmod(helper.c_str(), 0755);
   ExecTestHelper eth;
diff --git a/tests/libs/heap_tagging_helper.cpp b/tests/libs/heap_tagging_helper.cpp
index 1a970f2..16a8c8b 100644
--- a/tests/libs/heap_tagging_helper.cpp
+++ b/tests/libs/heap_tagging_helper.cpp
@@ -16,7 +16,9 @@
 
 #include <signal.h>
 #include <stdio.h>
+#include <sys/auxv.h>
 #include <sys/cdefs.h>
+#include <sys/mman.h>
 #include <unistd.h>
 #include <memory>
 
@@ -37,6 +39,11 @@
   _exit(0);
 }
 
+void action2(int signo, siginfo_t* info __unused, void*) {
+  fprintf(stderr, "unexpected signal %d\n", signo);
+  _exit(0);
+}
+
 __attribute__((optnone)) int main() {
   struct sigaction sa = {};
   sa.sa_sigaction = action;
@@ -47,6 +54,37 @@
   volatile int oob = p[-1];
   (void)oob;
 
+#if defined(__BIONIC__) && defined(__aarch64__)
+  // If we get here, bad access on system heap memory did not trigger a fault.
+  // This suggests that MTE is disabled. Make sure that explicitly tagged PROT_MTE memory does not
+  // trigger a fault either.
+  if (getauxval(AT_HWCAP2) & HWCAP2_MTE) {
+    sa.sa_sigaction = action2;
+    sigaction(SIGSEGV, &sa, nullptr);
+
+    size_t page_size = static_cast<size_t>(sysconf(_SC_PAGESIZE));
+    void* p = mmap(nullptr, page_size, PROT_READ | PROT_WRITE | PROT_MTE,
+                   MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+    if (!p) {
+      fprintf(stderr, "mmap failed\n");
+      return 1;
+    }
+
+    void* q = p;
+    __asm__ __volatile__(
+        ".arch_extension memtag\n"
+        "irg %[Ptr], %[Ptr], xzr\n"
+        "stg %[Ptr], [%[Ptr]]\n"
+        "addg %[Ptr], %[Ptr], 0, 1\n"
+        "str xzr, [%[Ptr]]\n"
+        : [Ptr] "+&r"(q)
+        :
+        : "memory");
+
+    munmap(p, page_size);
+  }
+#endif  // __aarch64__
+
   fprintf(stderr, "normal exit\n");
   return 0;
 }