[MTE] test for exception cleanup.

This depends on the LLVM change https://reviews.llvm.org/D135639.

Test: adb shell /data/local/tmp/stack_tagging_helper exception_cleanup
Bug: 174878242
Change-Id: Ia1dfdbe482b40c174acaf6c1ac4ad054470d10b8
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 0c9a2e0..8ae2257 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -1661,6 +1661,7 @@
      },
    },
    header_libs: ["bionic_libc_platform_headers"],
+   cflags: ["-fexceptions"],
 }
 
 cc_test {
@@ -1676,6 +1677,7 @@
      },
    },
    header_libs: ["bionic_libc_platform_headers"],
+   cflags: ["-fexceptions"],
 }
 
 cc_genrule {
diff --git a/tests/libs/stack_tagging_helper.cpp b/tests/libs/stack_tagging_helper.cpp
index a239dc1..d29844d 100644
--- a/tests/libs/stack_tagging_helper.cpp
+++ b/tests/libs/stack_tagging_helper.cpp
@@ -260,6 +260,81 @@
   CHECK(memtag_stack);
 }
 
+static uintptr_t GetTag(void* addr) {
+  return reinterpret_cast<uintptr_t>(addr) & (0xFULL << 56);
+}
+
+static uintptr_t GetTag(volatile void* addr) {
+  return GetTag(const_cast<void*>(addr));
+}
+
+static volatile char* throw_frame;
+static volatile char* skip_frame3_frame;
+volatile char *x;
+
+__attribute__((noinline)) void throws() {
+  // Prevent optimization.
+  if (getpid() == 0) return;
+  throw_frame = reinterpret_cast<char*>(__builtin_frame_address(0));
+  throw "error";
+}
+
+__attribute__((noinline)) void maybe_throws() {
+  // These are all unique sizes so in case of a failure, we can see which ones
+  // are not untagged from the tag dump.
+  volatile char y[5 * 16]= {};
+  x = y;
+  // Make sure y is tagged.
+  CHECK(GetTag(&y) != GetTag(__builtin_frame_address(0)));
+  throws();
+}
+
+__attribute__((noinline, no_sanitize("memtag"))) void skip_frame() {
+  volatile char y[6*16] = {};
+  x = y;
+  // Make sure y is not tagged.
+  CHECK(GetTag(&y) == GetTag(__builtin_frame_address(0)));
+  maybe_throws();
+}
+
+__attribute__((noinline)) void skip_frame2() {
+  volatile char y[7*16] = {};
+  x = y;
+  // Make sure y is tagged.
+  CHECK(GetTag(&y) != GetTag(__builtin_frame_address(0)));
+  skip_frame();
+}
+
+__attribute__((noinline, no_sanitize("memtag"))) void skip_frame3() {
+  volatile char y[8*16] = {};
+  x = y;
+  skip_frame3_frame = reinterpret_cast<char*>(__builtin_frame_address(0));
+  // Make sure y is not tagged.
+  CHECK(GetTag(&y) == GetTag(__builtin_frame_address(0)));
+  skip_frame2();
+}
+
+void test_exception_cleanup() {
+  // This is here for debugging purposes, if something goes wrong we can
+  // verify that this placeholder did not get untagged.
+  volatile char placeholder[16*16] = {};
+  x = placeholder;
+  try {
+    skip_frame3();
+  } catch (const char* e) {
+  }
+  if (throw_frame >= skip_frame3_frame) {
+    fprintf(stderr, "invalid throw frame");
+    exit(1);
+  }
+  for (char* b = const_cast<char*>(throw_frame); b < skip_frame3_frame; ++b) {
+    if (mte_get_tag(b) != b) {
+      fprintf(stderr, "invalid tag at %p", b);
+      exit(1);
+    }
+  }
+}
+
 int main(int argc, char** argv) {
   if (argc < 2) {
     printf("nothing to do\n");
@@ -296,6 +371,11 @@
     return 0;
   }
 
+  if (strcmp(argv[1], "exception_cleanup") == 0) {
+    test_exception_cleanup();
+    return 0;
+  }
+
   printf("unrecognized command: %s\n", argv[1]);
   return 1;
 }
diff --git a/tests/memtag_stack_test.cpp b/tests/memtag_stack_test.cpp
index 84ee8d1..97084ec 100644
--- a/tests/memtag_stack_test.cpp
+++ b/tests/memtag_stack_test.cpp
@@ -44,13 +44,13 @@
 #endif
 }
 
-INSTANTIATE_TEST_SUITE_P(, MemtagStackTest,
-                         testing::Combine(testing::Values("vfork_execve", "vfork_execl",
-                                                          "vfork_exit", "longjmp",
-                                                          "longjmp_sigaltstack", "android_mallopt"),
-                                          testing::Bool()),
-                         [](const ::testing::TestParamInfo<MemtagStackTest::ParamType>& info) {
-                           std::string s = std::get<0>(info.param);
-                           if (std::get<1>(info.param)) s += "_static";
-                           return s;
-                         });
+INSTANTIATE_TEST_SUITE_P(
+    , MemtagStackTest,
+    testing::Combine(testing::Values("vfork_execve", "vfork_execl", "vfork_exit", "longjmp",
+                                     "longjmp_sigaltstack", "android_mallopt", "exception_cleanup"),
+                     testing::Bool()),
+    [](const ::testing::TestParamInfo<MemtagStackTest::ParamType>& info) {
+      std::string s = std::get<0>(info.param);
+      if (std::get<1>(info.param)) s += "_static";
+      return s;
+    });