Add tagged pointers to bionic.
This patch introduces tagged pointers to bionic. We add a static tag to
all pointers on arm64 compatible platforms (needs requisite
top-byte-ignore hardware feature and relevant kernel patches).
We dynamically detect TBI-compatible devices (a device with the TBI feature and
kernel support) at process start time, and insert an implementation-dependent
tag into the top byte of the pointer for all heap allocations. We then check
that the tag has not been truncated when deallocating the memory.
If an application incorrectly writes to the top byte of the pointer, we
terminate the process at time of detection. This will allow MTE-incompatible
applications to be caught early.
Bug: 135754954
Bug: 147147490
Test: cd bionic && atest .
Change-Id: Ie424325ba1e3c4443040ac265aeaa28d9e405d28
diff --git a/tests/Android.bp b/tests/Android.bp
index ee4f02e..d3769db 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -211,6 +211,9 @@
"libprocinfo",
"libsystemproperties",
],
+ srcs: [
+ "tagged_pointers_test.cpp",
+ ],
},
},
diff --git a/tests/tagged_pointers_test.cpp b/tests/tagged_pointers_test.cpp
new file mode 100644
index 0000000..4a666c4
--- /dev/null
+++ b/tests/tagged_pointers_test.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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 "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__
+ 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");
+
+ 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__)
+}
diff --git a/tests/utils.h b/tests/utils.h
index cfc68c9..5014ef7 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -68,10 +68,8 @@
static inline void* untag_address(void* addr) {
#if defined(__LP64__)
- if (running_with_hwasan()) {
- constexpr uintptr_t mask = (static_cast<uintptr_t>(1) << 56) - 1;
- addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & mask);
- }
+ constexpr uintptr_t mask = (static_cast<uintptr_t>(1) << 56) - 1;
+ addr = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(addr) & mask);
#endif
return addr;
}