Introduce a new heap tagging level, M_HEAP_TAGGING_LEVEL_SYNC.
The SYNC tagging level enables stack trace collection for allocations and
deallocations, which allows allocation and deallocation stack traces to
appear in tombstones when encountering a tag check fault in synchronous tag
checking mode.
Bug: 135772972
Change-Id: Ibda9f51b29d2c8e2c993fc74425dea7bfa23ab1e
diff --git a/tests/Android.bp b/tests/Android.bp
index c751084..1f45595 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -117,6 +117,7 @@
"glob_test.cpp",
"grp_pwd_test.cpp",
"grp_pwd_file_test.cpp",
+ "heap_tagging_level_test.cpp",
"iconv_test.cpp",
"ifaddrs_test.cpp",
"ifunc_test.cpp",
@@ -231,9 +232,6 @@
"libprocinfo",
"libsystemproperties",
],
- srcs: [
- "tagged_pointers_test.cpp",
- ],
},
},
diff --git a/tests/heap_tagging_level_test.cpp b/tests/heap_tagging_level_test.cpp
new file mode 100644
index 0000000..05123fd
--- /dev/null
+++ b/tests/heap_tagging_level_test.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 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 <gtest/gtest.h>
+#include <sys/prctl.h>
+
+#if defined(__BIONIC__)
+#include "platform/bionic/malloc.h"
+#include "platform/bionic/mte.h"
+#include "utils.h"
+
+#include "SignalUtils.h"
+
+#include <bionic/malloc_tagged_pointers.h>
+
+static bool KernelSupportsTaggedPointers() {
+#ifdef __aarch64__
+ int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
+#else
+ return false;
+#endif
+}
+
+static bool SetHeapTaggingLevel(HeapTaggingLevel level) {
+ return android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &level, sizeof(level));
+}
+#endif
+
+TEST(heap_tagging_level, tagged_pointer_dies) {
+#if defined(__BIONIC__)
+ if (!KernelSupportsTaggedPointers()) {
+ GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
+ }
+
+#ifdef __aarch64__
+ if (mte_supported()) {
+ GTEST_SKIP() << "Tagged pointers are not used on MTE hardware.";
+ }
+
+ void *x = malloc(1);
+
+ // Ensure that `x` has a pointer tag.
+ EXPECT_NE(reinterpret_cast<uintptr_t>(x) >> 56, 0u);
+
+ x = untag_address(x);
+ EXPECT_DEATH(free(x), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
+
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
+ EXPECT_DEATH(free(untag_address(malloc(1))), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
+
+ x = malloc(1);
+ void *y = malloc(1);
+ // Disable heap tagging.
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
+ // Ensure an older tagged pointer can still be freed.
+ free(x);
+ // Tag mismatch is not detected on old pointers.
+ free(untag_address(y));
+#endif // defined(__aarch64__)
+#else
+ GTEST_SKIP() << "bionic-only test";
+#endif // defined(__BIONIC__)
+}
+
+#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+template <int SiCode> void CheckSiCode(int, siginfo_t* info, void*) {
+ if (info->si_code != SiCode) {
+ _exit(2);
+ }
+ _exit(1);
+}
+
+static bool SetTagCheckingLevel(int level) {
+ int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ if (tagged_addr_ctrl < 0) {
+ return false;
+ }
+
+ tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level;
+ return prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) == 0;
+}
+#endif
+
+TEST(heap_tagging_level, sync_async_bad_accesses_die) {
+#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
+ GTEST_SKIP() << "requires MTE support";
+ }
+
+ std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
+
+ // First, check that memory tagging is enabled and the default tag checking level is async.
+ // We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a
+ // mismatching tag before each allocation.
+ EXPECT_EXIT(
+ {
+ ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTEAERR>, SA_SIGINFO);
+ p[-1] = 42;
+ },
+ testing::ExitedWithCode(1), "");
+
+ EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_SYNC));
+ EXPECT_EXIT(
+ {
+ ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTESERR>, SA_SIGINFO);
+ p[-1] = 42;
+ },
+ testing::ExitedWithCode(1), "");
+
+ EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
+ volatile int oob ATTRIBUTE_UNUSED = p[-1];
+#endif
+}
+
+TEST(heap_tagging_level, none_pointers_untagged) {
+#if defined(__BIONIC__)
+#if defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
+ EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
+#endif
+
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
+ std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
+ EXPECT_EQ(untag_address(p.get()), p.get());
+#else
+ GTEST_SKIP() << "bionic-only test";
+#endif
+}
+
+TEST(heap_tagging_level, tagging_level_transitions) {
+#if defined(__BIONIC__) && defined(__aarch64__)
+ if (!KernelSupportsTaggedPointers()) {
+ GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
+ }
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+ EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
+#endif
+
+ EXPECT_FALSE(SetHeapTaggingLevel(static_cast<HeapTaggingLevel>(12345)));
+
+ if (mte_supported()) {
+ // ASYNC -> ...
+ EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
+
+ // SYNC -> ...
+ EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
+ } else {
+ // TBI -> ...
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
+ EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
+ EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
+ }
+
+ // TBI -> NONE on non-MTE, ASYNC -> NONE on MTE.
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
+
+ // NONE -> ...
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
+ EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_TBI));
+ EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC));
+ EXPECT_FALSE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
+#else
+ GTEST_SKIP() << "bionic/arm64 only";
+#endif
+}
+
+TEST(heap_tagging_level, tagging_level_transition_sync_none) {
+#if defined(__BIONIC__) && defined(__aarch64__)
+ // We can't test SYNC -> NONE in tagging_level_transitions because we can only make one transition
+ // to NONE (which we use to test ASYNC -> NONE), so we test it here separately.
+ if (!mte_supported()) {
+ GTEST_SKIP() << "requires MTE support";
+ }
+
+#if defined(ANDROID_EXPERIMENTAL_MTE)
+ EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
+#endif
+
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_SYNC));
+ EXPECT_TRUE(SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_NONE));
+#else
+ GTEST_SKIP() << "bionic/arm64 only";
+#endif
+}
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index a2f310e..47a9033 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -1201,69 +1201,3 @@
GTEST_SKIP() << "bionic extension";
#endif
}
-
-#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
-template <int SiCode> void CheckSiCode(int, siginfo_t* info, void*) {
- if (info->si_code != SiCode) {
- _exit(2);
- }
- _exit(1);
-}
-
-static bool SetTagCheckingLevel(int level) {
- int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
- if (tagged_addr_ctrl < 0) {
- return false;
- }
-
- tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | level;
- return prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) == 0;
-}
-#endif
-
-TEST(android_mallopt, tag_level) {
-#if defined(__BIONIC__) && defined(__aarch64__) && defined(ANDROID_EXPERIMENTAL_MTE)
- if (!(getauxval(AT_HWCAP2) & HWCAP2_MTE)) {
- GTEST_SKIP() << "requires MTE support";
- }
-
- std::unique_ptr<int[]> p = std::make_unique<int[]>(4);
-
- // First, check that memory tagging is enabled and the default tag checking level is async.
- // We assume that scudo is used on all MTE enabled hardware; scudo inserts a header with a
- // mismatching tag before each allocation.
- EXPECT_EXIT(
- {
- ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTEAERR>, SA_SIGINFO);
- p[-1] = 42;
- },
- testing::ExitedWithCode(1), "");
-
- EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_SYNC));
- EXPECT_EXIT(
- {
- ScopedSignalHandler ssh(SIGSEGV, CheckSiCode<SEGV_MTESERR>, SA_SIGINFO);
- p[-1] = 42;
- },
- testing::ExitedWithCode(1), "");
-
- EXPECT_TRUE(SetTagCheckingLevel(PR_MTE_TCF_NONE));
- volatile int oob ATTRIBUTE_UNUSED = p[-1];
-
- HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_TBI;
- EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
-
- tag_level = M_HEAP_TAGGING_LEVEL_NONE;
- EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
- std::unique_ptr<int[]> p2 = std::make_unique<int[]>(4);
- EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(p2.get()) >> 56);
-
- tag_level = M_HEAP_TAGGING_LEVEL_ASYNC;
- EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
-
- tag_level = M_HEAP_TAGGING_LEVEL_NONE;
- EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
-#else
- GTEST_SKIP() << "arm64 only";
-#endif
-}
diff --git a/tests/tagged_pointers_test.cpp b/tests/tagged_pointers_test.cpp
deleted file mode 100644
index 56d1037..0000000
--- a/tests/tagged_pointers_test.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2014 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 <gtest/gtest.h>
-#include <sys/prctl.h>
-
-#include "platform/bionic/malloc.h"
-#include "platform/bionic/mte.h"
-#include "utils.h"
-
-#include <bionic/malloc_tagged_pointers.h>
-
-static bool KernelSupportsTaggedPointers() {
-#ifdef __aarch64__
-#define PR_SET_TAGGED_ADDR_CTRL 55
-#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
- int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
- return res >= 0 && res & PR_TAGGED_ADDR_ENABLE;
-#else
- return false;
-#endif
-}
-
-TEST(tagged_pointers, check_tagged_pointer_dies) {
- if (!KernelSupportsTaggedPointers()) {
- GTEST_SKIP() << "Kernel doesn't support tagged pointers.";
- }
-
-#ifdef __aarch64__
- if (mte_supported()) {
- GTEST_SKIP() << "Tagged pointers are not used on MTE hardware.";
- }
-
- void *x = malloc(1);
-
- // Ensure that `x` has a pointer tag.
- EXPECT_NE(reinterpret_cast<uintptr_t>(x) >> 56, 0u);
-
- x = untag_address(x);
- EXPECT_DEATH(free(x), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
-
- HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_TBI;
- EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
- EXPECT_DEATH(free(untag_address(malloc(1))), "Pointer tag for 0x[a-zA-Z0-9]* was truncated");
-
- tag_level = M_HEAP_TAGGING_LEVEL_ASYNC;
- EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
-
- x = malloc(1);
- void *y = malloc(1);
- // Disable heap tagging.
- tag_level = M_HEAP_TAGGING_LEVEL_NONE;
- EXPECT_TRUE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
- // Ensure an older tagged pointer can still be freed.
- free(x);
- // Tag mismatch is not detected on old pointers.
- free(untag_address(y));
- // New pointers are not tagged.
- x = malloc(1);
- EXPECT_EQ(untag_address(x), x);
- free(x);
-
- // Switching back to checked mode is not possible.
- tag_level = M_HEAP_TAGGING_LEVEL_TBI;
- EXPECT_FALSE(android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level)));
- // We remain in the unchecked mode.
- x = malloc(1);
- EXPECT_EQ(untag_address(x), x);
- free(x);
-#endif // defined(__aarch64__)
-}