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: I6e5b809fc81f55dd517f845eaf20f3c0ebd4d86e
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index d64a6bd..ce97f31 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -27,6 +27,7 @@
  */
 
 #include "libc_init_common.h"
+#include "malloc_tagged_pointers.h"
 
 #include <elf.h>
 #include <errno.h>
@@ -39,6 +40,7 @@
 #include <sys/auxv.h>
 #include <sys/personality.h>
 #include <sys/time.h>
+#include <sys/utsname.h>
 #include <unistd.h>
 
 #include <async_safe/log.h>
@@ -58,6 +60,24 @@
 // Not public, but well-known in the BSDs.
 const char* __progname;
 
+
+#ifdef __aarch64__
+static bool KernelSupportsTaggedPointers() {
+  utsname buf;
+  utsname* tagged_buf =
+      reinterpret_cast<utsname*>(reinterpret_cast<uintptr_t>(&buf) |
+      (static_cast<uintptr_t>(0xAA) << TAG_SHIFT));
+  // We use `uname()` here as a system call to determine if the kernel supports
+  // tagged pointers. If the kernel supports tagged points, it will truncate the
+  // tag before populating `buf`, and `uname()` should return zero (indicating
+  // no error). If ARM TBI isn't enabled, the kernel should return an error code
+  // that indicates that the tagged memory couldn't be accessed. The exact
+  // system call that we use here isn't important, it's just a convenient system
+  // call that validates a pointer.
+  return uname(tagged_buf) == 0;
+}
+#endif
+
 void __libc_init_globals() {
   // Initialize libc globals that are needed in both the linker and in libc.
   // In dynamic binaries, this is run at least twice for different copies of the
@@ -66,6 +86,13 @@
   __libc_globals.mutate([](libc_globals* globals) {
     __libc_init_vdso(globals);
     __libc_init_setjmp_cookie(globals);
+#ifdef __aarch64__
+    globals->heap_pointer_tag = KernelSupportsTaggedPointers()
+                                ? (reinterpret_cast<uintptr_t>(POINTER_TAG) << TAG_SHIFT)
+                                : 0;
+#else
+    globals->heap_pointer_tag = 0;
+#endif
   });
 }