Merge "Soft-enables MAC address restrictions."
diff --git a/docs/status.md b/docs/status.md
index 5470668..fb88dd2 100644
--- a/docs/status.md
+++ b/docs/status.md
@@ -33,7 +33,11 @@
 Missing functionality:
   * `<aio.h>`
   * `<wordexp.h>`
-  * Thread cancellation (`pthread_cancel`).
+  * Thread cancellation (`pthread_cancel`). Unlikely to ever be implemented
+    because of the difficulty and cost of implementing it, and the difficulty
+    of using it correctly. See
+    [This is why we can't have safe cancellation points](https://lwn.net/Articles/683118/)
+    for more about thread cancellation.
   * Robust mutexes
 
 Run `./libc/tools/check-symbols-glibc.py` in bionic/ for the current
diff --git a/libc/Android.bp b/libc/Android.bp
index c764cb9..0950ebd 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -71,6 +71,7 @@
         fuzzer: false,
     },
     native_coverage: false,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
@@ -359,7 +360,6 @@
     defaults: ["libc_defaults"],
     srcs: [
         "upstream-freebsd/lib/libc/gen/glob.c",
-        "upstream-freebsd/lib/libc/stdlib/realpath.c",
     ],
 
     cflags: [
@@ -1179,6 +1179,7 @@
         "bionic/raise.cpp",
         "bionic/rand.cpp",
         "bionic/readlink.cpp",
+        "bionic/realpath.cpp",
         "bionic/reboot.cpp",
         "bionic/recv.cpp",
         "bionic/rename.cpp",
@@ -1402,6 +1403,7 @@
         "libc_defaults",
         "libc_native_allocator_defaults",
     ],
+    ramdisk_available: false,
     srcs: libc_common_src_files + [
         "bionic/heap_tagging.cpp",
         "bionic/malloc_common.cpp",
@@ -1823,6 +1825,7 @@
     ],
     host_supported: true,
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     export_include_dirs: [
@@ -1839,6 +1842,7 @@
 
     host_supported: true,
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
@@ -1979,6 +1983,7 @@
     name: "crt_defaults",
     defaults: ["linux_bionic_supported"],
     vendor_available: true,
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
diff --git a/libc/NOTICE b/libc/NOTICE
index 51b00ad..87d39c9 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -3809,34 +3809,6 @@
 
 -------------------------------------------------------------------
 
-Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
-   notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
-   notice, this list of conditions and the following disclaimer in the
-   documentation and/or other materials provided with the distribution.
-3. The names of the authors may not be used to endorse or promote
-   products derived from this software without specific prior written
-   permission.
-
-THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
 Copyright (c) 2003 Dag-Erling Smørgrav
 All rights reserved.
 
diff --git a/libc/async_safe/Android.bp b/libc/async_safe/Android.bp
index c28d53a..7df6ab9 100644
--- a/libc/async_safe/Android.bp
+++ b/libc/async_safe/Android.bp
@@ -22,6 +22,7 @@
 
 cc_library_headers {
     name: "libasync_safe_headers",
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     defaults: ["linux_bionic_supported"],
diff --git a/libc/bionic/heap_tagging.cpp b/libc/bionic/heap_tagging.cpp
index 7c30c19..0eaf34e 100644
--- a/libc/bionic/heap_tagging.cpp
+++ b/libc/bionic/heap_tagging.cpp
@@ -28,11 +28,38 @@
 
 #include "heap_tagging.h"
 #include "malloc_common.h"
+#include "malloc_tagged_pointers.h"
 
 #include <platform/bionic/malloc.h>
+#include <platform/bionic/mte_kernel.h>
 
 static HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
 
+void SetDefaultHeapTaggingLevel() {
+  // Allow the kernel to accept tagged pointers in syscall arguments. This is a no-op (kernel
+  // returns -EINVAL) if the kernel doesn't understand the prctl.
+#if defined(__aarch64__)
+#define PR_SET_TAGGED_ADDR_CTRL 55
+#define PR_TAGGED_ADDR_ENABLE (1UL << 0)
+#ifdef ANDROID_EXPERIMENTAL_MTE
+  // First, try enabling MTE in asynchronous mode, with tag 0 excluded. This will fail if the kernel
+  // or hardware doesn't support MTE, and we will fall back to just enabling tagged pointers in
+  // syscall arguments.
+  if (prctl(PR_SET_TAGGED_ADDR_CTRL,
+            PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC | (1 << PR_MTE_EXCL_SHIFT), 0, 0, 0) == 0) {
+    return;
+  }
+#endif // ANDROID_EXPERIMENTAL_MTE
+
+  if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == 0) {
+    heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
+    __libc_globals.mutate([](libc_globals* globals) {
+      globals->heap_pointer_tag = reinterpret_cast<uintptr_t>(POINTER_TAG) << TAG_SHIFT;
+    });
+  }
+#endif  // aarch64
+}
+
 bool SetHeapTaggingLevel(void* arg, size_t arg_size) {
   if (arg_size != sizeof(HeapTaggingLevel)) {
     return false;
@@ -42,11 +69,23 @@
   switch (tag_level) {
     case M_HEAP_TAGGING_LEVEL_NONE:
       break;
+    case M_HEAP_TAGGING_LEVEL_TBI:
+      if (heap_tagging_level == M_HEAP_TAGGING_LEVEL_NONE) {
+        error_log(
+            "SetHeapTaggingLevel: re-enabling tagging after it was disabled is not supported");
+        return false;
+      }
+      break;
     default:
       error_log("SetHeapTaggingLevel: unknown tagging level");
       return false;
   }
   heap_tagging_level = tag_level;
   info_log("SetHeapTaggingLevel: tag level set to %d", tag_level);
+
+  if (heap_tagging_level == M_HEAP_TAGGING_LEVEL_NONE && __libc_globals->heap_pointer_tag != 0) {
+    __libc_globals.mutate([](libc_globals* globals) { globals->heap_pointer_tag = 0; });
+  }
+
   return true;
 }
diff --git a/libc/bionic/heap_tagging.h b/libc/bionic/heap_tagging.h
index da4c68c..2aaf608 100644
--- a/libc/bionic/heap_tagging.h
+++ b/libc/bionic/heap_tagging.h
@@ -30,4 +30,5 @@
 
 #include <stddef.h>
 
+void SetDefaultHeapTaggingLevel();
 bool SetHeapTaggingLevel(void* arg, size_t arg_size);
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index d64a6bd..e4106e9 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 "heap_tagging.h"
 
 #include <elf.h>
 #include <errno.h>
@@ -42,7 +43,6 @@
 #include <unistd.h>
 
 #include <async_safe/log.h>
-#include <platform/bionic/mte_kernel.h>
 
 #include "private/WriteProtected.h"
 #include "private/bionic_defs.h"
@@ -94,7 +94,7 @@
   // Initialize various globals.
   environ = __libc_shared_globals()->init_environ;
   errno = 0;
-  __progname = __libc_shared_globals()->init_progname ?: "<unknown>";
+  setprogname(__libc_shared_globals()->init_progname ?: "<unknown>");
 
 #if !defined(__LP64__)
   __check_max_thread_id();
@@ -105,23 +105,7 @@
   __system_properties_init(); // Requires 'environ'.
   __libc_init_fdsan(); // Requires system properties (for debug.fdsan).
 
-  // Allow the kernel to accept tagged pointers in syscall arguments. This is a no-op (kernel
-  // returns -EINVAL) if the kernel doesn't understand the prctl.
-#if defined(__aarch64__)
-#define PR_SET_TAGGED_ADDR_CTRL 55
-#define PR_TAGGED_ADDR_ENABLE   (1UL << 0)
-#ifdef ANDROID_EXPERIMENTAL_MTE
-  // First, try enabling MTE in asynchronous mode, with tag 0 excluded. This will fail if the kernel
-  // or hardware doesn't support MTE, and we will fall back to just enabling tagged pointers in
-  // syscall arguments.
-  if (prctl(PR_SET_TAGGED_ADDR_CTRL,
-            PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC | (1 << PR_MTE_EXCL_SHIFT), 0, 0, 0)) {
-    prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0);
-  }
-#else
-  prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0);
-#endif
-#endif
+  SetDefaultHeapTaggingLevel();
 }
 
 void __libc_init_fork_handler() {
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 30c9cc7..e08083f 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -44,6 +44,7 @@
 #include "heap_tagging.h"
 #include "malloc_common.h"
 #include "malloc_limit.h"
+#include "malloc_tagged_pointers.h"
 
 // =============================================================================
 // Global variables instantations.
@@ -62,17 +63,18 @@
 extern "C" void* calloc(size_t n_elements, size_t elem_size) {
   auto dispatch_table = GetDispatchTable();
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->calloc(n_elements, elem_size);
+    return MaybeTagPointer(dispatch_table->calloc(n_elements, elem_size));
   }
   void* result = Malloc(calloc)(n_elements, elem_size);
   if (__predict_false(result == nullptr)) {
     warning_log("calloc(%zu, %zu) failed: returning null pointer", n_elements, elem_size);
   }
-  return result;
+  return MaybeTagPointer(result);
 }
 
 extern "C" void free(void* mem) {
   auto dispatch_table = GetDispatchTable();
+  mem = MaybeUntagAndCheckPointer(mem);
   if (__predict_false(dispatch_table != nullptr)) {
     dispatch_table->free(mem);
   } else {
@@ -106,18 +108,22 @@
 
 extern "C" void* malloc(size_t bytes) {
   auto dispatch_table = GetDispatchTable();
+  void *result;
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->malloc(bytes);
+    result = dispatch_table->malloc(bytes);
+  } else {
+    result = Malloc(malloc)(bytes);
   }
-  void* result = Malloc(malloc)(bytes);
   if (__predict_false(result == nullptr)) {
     warning_log("malloc(%zu) failed: returning null pointer", bytes);
+    return nullptr;
   }
-  return result;
+  return MaybeTagPointer(result);
 }
 
 extern "C" size_t malloc_usable_size(const void* mem) {
   auto dispatch_table = GetDispatchTable();
+  mem = MaybeUntagAndCheckPointer(mem);
   if (__predict_false(dispatch_table != nullptr)) {
     return dispatch_table->malloc_usable_size(mem);
   }
@@ -127,45 +133,52 @@
 extern "C" void* memalign(size_t alignment, size_t bytes) {
   auto dispatch_table = GetDispatchTable();
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->memalign(alignment, bytes);
+    return MaybeTagPointer(dispatch_table->memalign(alignment, bytes));
   }
   void* result = Malloc(memalign)(alignment, bytes);
   if (__predict_false(result == nullptr)) {
     warning_log("memalign(%zu, %zu) failed: returning null pointer", alignment, bytes);
   }
-  return result;
+  return MaybeTagPointer(result);
 }
 
 extern "C" int posix_memalign(void** memptr, size_t alignment, size_t size) {
   auto dispatch_table = GetDispatchTable();
+  int result;
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->posix_memalign(memptr, alignment, size);
+    result = dispatch_table->posix_memalign(memptr, alignment, size);
+  } else {
+    result = Malloc(posix_memalign)(memptr, alignment, size);
   }
-  return Malloc(posix_memalign)(memptr, alignment, size);
+  if (result == 0) {
+    *memptr = MaybeTagPointer(*memptr);
+  }
+  return result;
 }
 
 extern "C" void* aligned_alloc(size_t alignment, size_t size) {
   auto dispatch_table = GetDispatchTable();
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->aligned_alloc(alignment, size);
+    return MaybeTagPointer(dispatch_table->aligned_alloc(alignment, size));
   }
   void* result = Malloc(aligned_alloc)(alignment, size);
   if (__predict_false(result == nullptr)) {
     warning_log("aligned_alloc(%zu, %zu) failed: returning null pointer", alignment, size);
   }
-  return result;
+  return MaybeTagPointer(result);
 }
 
 extern "C" __attribute__((__noinline__)) void* realloc(void* old_mem, size_t bytes) {
   auto dispatch_table = GetDispatchTable();
+  old_mem = MaybeUntagAndCheckPointer(old_mem);
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->realloc(old_mem, bytes);
+    return MaybeTagPointer(dispatch_table->realloc(old_mem, bytes));
   }
   void* result = Malloc(realloc)(old_mem, bytes);
   if (__predict_false(result == nullptr && bytes != 0)) {
     warning_log("realloc(%p, %zu) failed: returning null pointer", old_mem, bytes);
   }
-  return result;
+  return MaybeTagPointer(result);
 }
 
 extern "C" void* reallocarray(void* old_mem, size_t item_count, size_t item_size) {
@@ -183,42 +196,66 @@
 extern "C" void* pvalloc(size_t bytes) {
   auto dispatch_table = GetDispatchTable();
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->pvalloc(bytes);
+    return MaybeTagPointer(dispatch_table->pvalloc(bytes));
   }
   void* result = Malloc(pvalloc)(bytes);
   if (__predict_false(result == nullptr)) {
     warning_log("pvalloc(%zu) failed: returning null pointer", bytes);
   }
-  return result;
+  return MaybeTagPointer(result);
 }
 
 extern "C" void* valloc(size_t bytes) {
   auto dispatch_table = GetDispatchTable();
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->valloc(bytes);
+    return MaybeTagPointer(dispatch_table->valloc(bytes));
   }
   void* result = Malloc(valloc)(bytes);
   if (__predict_false(result == nullptr)) {
     warning_log("valloc(%zu) failed: returning null pointer", bytes);
   }
-  return result;
+  return MaybeTagPointer(result);
 }
 #endif
 // =============================================================================
 
+struct CallbackWrapperArg {
+  void (*callback)(uintptr_t base, size_t size, void* arg);
+  void* arg;
+};
+
+void CallbackWrapper(uintptr_t base, size_t size, void* arg) {
+  CallbackWrapperArg* wrapper_arg = reinterpret_cast<CallbackWrapperArg*>(arg);
+  wrapper_arg->callback(
+    reinterpret_cast<uintptr_t>(MaybeTagPointer(reinterpret_cast<void*>(base))),
+    size, wrapper_arg->arg);
+}
+
 // =============================================================================
 // Exported for use by libmemunreachable.
 // =============================================================================
 
 // Calls callback for every allocation in the anonymous heap mapping
-// [base, base+size).  Must be called between malloc_disable and malloc_enable.
+// [base, base+size). Must be called between malloc_disable and malloc_enable.
+// `base` in this can take either a tagged or untagged pointer, but we always
+// provide a tagged pointer to the `base` argument of `callback` if the kernel
+// supports tagged pointers.
 extern "C" int malloc_iterate(uintptr_t base, size_t size,
     void (*callback)(uintptr_t base, size_t size, void* arg), void* arg) {
   auto dispatch_table = GetDispatchTable();
+  // Wrap the malloc_iterate callback we were provided, in order to provide
+  // pointer tagging support.
+  CallbackWrapperArg wrapper_arg;
+  wrapper_arg.callback = callback;
+  wrapper_arg.arg = arg;
+  uintptr_t untagged_base =
+      reinterpret_cast<uintptr_t>(UntagPointer(reinterpret_cast<void*>(base)));
   if (__predict_false(dispatch_table != nullptr)) {
-    return dispatch_table->malloc_iterate(base, size, callback, arg);
+    return dispatch_table->malloc_iterate(
+      untagged_base, size, CallbackWrapper, &wrapper_arg);
   }
-  return Malloc(malloc_iterate)(base, size, callback, arg);
+  return Malloc(malloc_iterate)(
+    untagged_base, size, CallbackWrapper, &wrapper_arg);
 }
 
 // Disable calls to malloc so malloc_iterate gets a consistent view of
diff --git a/libc/bionic/malloc_tagged_pointers.h b/libc/bionic/malloc_tagged_pointers.h
new file mode 100644
index 0000000..9c2a89b
--- /dev/null
+++ b/libc/bionic/malloc_tagged_pointers.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/reboot.h>
+#include <unistd.h>
+
+#include <async_safe/log.h>
+#include <private/bionic_globals.h>
+
+// We choose a static pointer tag here for performance reasons. Dynamic tagging
+// doesn't improve our detection, and simply hurts performance. This tag is
+// deliberately chosen to always point to inaccessible memory on a standard
+// 64-bit userspace process, and be easily identifiable by developers. This tag
+// is also deliberately different from the standard pattern-init tag (0xAA), as
+// to be distinguishable from an uninitialized-pointer access. The first and
+// second nibbles are also deliberately designed to be the bitset-mirror of each
+// other (0b1100, 0b0011) in order to reduce incidental matches. Users must not
+// rely on the implementation-defined value of this pointer tag, as it may
+// change.
+static constexpr uintptr_t POINTER_TAG = 0x3C;
+static constexpr unsigned TAG_SHIFT = 56;
+#if defined(__aarch64__)
+static constexpr uintptr_t ADDRESS_MASK = (static_cast<uintptr_t>(1) << TAG_SHIFT) - 1;
+static constexpr uintptr_t TAG_MASK = static_cast<uintptr_t>(0xFF) << TAG_SHIFT;
+#endif // defined(__aarch64__)
+
+// Return a forcibly-tagged pointer.
+static inline void* TagPointer(void* ptr) {
+#if defined(__aarch64__)
+  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) |
+                                 reinterpret_cast<uintptr_t>(__libc_globals->heap_pointer_tag));
+#else
+  async_safe_fatal("Attempting to tag a pointer (%p) on non-aarch64.", ptr);
+#endif
+}
+
+#if defined(__aarch64__) && !__has_feature(hwaddress_sanitizer)
+// Return a forcibly-untagged pointer. The pointer tag is not checked for
+// validity.
+static inline void* UntagPointer(const volatile void* ptr) {
+  return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(ptr) & ADDRESS_MASK);
+}
+
+static void* SlowPathPointerCheck(const volatile void* ptr) {
+  uintptr_t ptr_tag = reinterpret_cast<uintptr_t>(ptr) & TAG_MASK;
+  uintptr_t heap_tag = reinterpret_cast<uintptr_t>(__libc_globals->heap_pointer_tag);
+
+  // Applications may disable pointer tagging, which will be propagated to
+  // libc in the zygote. This means that there may already be tagged heap
+  // allocations that will fail when checked against the zero-ed heap tag. The
+  // check bellow allows us to turn *off* pointer tagging and still allow
+  // tagged heap allocations to be freed, as long as they're using *our* tag.
+  if (__predict_false(heap_tag != 0 || ptr_tag != (POINTER_TAG << TAG_SHIFT))) {
+    // TODO(b/145604058) - Upstream tagged pointers documentation and provide
+    // a link to it in the abort message here.
+    async_safe_fatal("Pointer tag for %p was truncated.", ptr);
+  }
+  return UntagPointer(ptr);
+}
+
+// Return a tagged pointer iff the kernel supports tagged pointers, and `ptr` is
+// non-null.
+static inline void* MaybeTagPointer(void* ptr) {
+  if (__predict_true(ptr != nullptr)) {
+    return TagPointer(ptr);
+  }
+  return ptr;
+}
+
+// Untag the pointer, and check the pointer tag iff the kernel supports tagged
+// pointers. If the tag is incorrect, trap.
+static inline void* MaybeUntagAndCheckPointer(const volatile void* ptr) {
+  if (__predict_false(ptr == nullptr)) {
+    return nullptr;
+  }
+
+  uintptr_t ptr_tag = reinterpret_cast<uintptr_t>(ptr) & TAG_MASK;
+  uintptr_t heap_tag = reinterpret_cast<uintptr_t>(__libc_globals->heap_pointer_tag);
+
+  if (__predict_false(heap_tag != ptr_tag)) {
+    return SlowPathPointerCheck(ptr);
+  }
+  return UntagPointer(ptr);
+}
+
+#else  // defined(__aarch64__) && !__has_feature(hwaddress_sanitizer)
+static inline void* UntagPointer(const volatile void* ptr) {
+  return const_cast<void*>(ptr);
+}
+
+static inline void* MaybeTagPointer(void* ptr) {
+  return ptr;
+}
+
+static inline void* MaybeUntagAndCheckPointer(const volatile void* ptr) {
+  return const_cast<void *>(ptr);
+}
+
+#endif  // defined(__aarch64__) && !__has_feature(hwaddress_sanitizer)
diff --git a/libc/bionic/realpath.cpp b/libc/bionic/realpath.cpp
new file mode 100644
index 0000000..e43d8e2
--- /dev/null
+++ b/libc/bionic/realpath.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "private/FdPath.h"
+#include "private/ScopedFd.h"
+
+// This function needs a 4KiB (PATH_MAX) buffer.
+// The alternative is to heap allocate and then trim, but that's 2x the code.
+// (Remember that readlink(2) won't tell you the needed size, so the multi-pass
+// algorithm isn't even an option unless you want to just guess, in which case
+// you're back needing to trim again.)
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
+char* realpath(const char* path, char* result) {
+  // Weird special case.
+  if (!path) {
+    errno = EINVAL;
+    return nullptr;
+  }
+
+  // Get an O_PATH fd, and...
+  ScopedFd fd(open(path, O_PATH | O_CLOEXEC));
+  if (fd.get() == -1) return nullptr;
+
+  // (...remember the device/inode that we're talking about and...)
+  struct stat sb;
+  if (fstat(fd.get(), &sb) == -1) return nullptr;
+  dev_t st_dev = sb.st_dev;
+  ino_t st_ino = sb.st_ino;
+
+  // ...ask the kernel to do the hard work for us.
+  FdPath fd_path(fd.get());
+  char dst[PATH_MAX];
+  ssize_t l = readlink(fd_path.c_str(), dst, sizeof(dst) - 1);
+  if (l == -1) return nullptr;
+  dst[l] = '\0';
+
+  // What if the file was removed in the meantime? readlink(2) will have
+  // returned "/a/b/c (deleted)", and we want to return ENOENT instead.
+  if (stat(dst, &sb) == -1 || st_dev != sb.st_dev || st_ino != sb.st_ino) {
+    errno = ENOENT;
+    return nullptr;
+  }
+
+  return result ? strcpy(result, dst) : strdup(dst);
+}
diff --git a/libc/malloc_debug/malloc_debug.cpp b/libc/malloc_debug/malloc_debug.cpp
index c0765a9..6eaac7d 100644
--- a/libc/malloc_debug/malloc_debug.cpp
+++ b/libc/malloc_debug/malloc_debug.cpp
@@ -43,6 +43,7 @@
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
+#include <bionic/malloc_tagged_pointers.h>
 #include <private/bionic_malloc_dispatch.h>
 #include <private/MallocXmlElem.h>
 
@@ -883,6 +884,7 @@
   if (!(g_debug->config().options() & BACKTRACE)) {
     return 0;
   }
+  pointer = UntagPointer(pointer);
   return PointerData::GetFrames(pointer, frames, max_frames);
 }
 
diff --git a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
index c405c7f..1298df7 100644
--- a/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_unit_tests.cpp
@@ -1532,7 +1532,8 @@
     BacktraceUnwindFake(
       std::vector<unwindstack::LocalFrameData>{{nullptr, 0x1100, 0x100, "fake1", 10},
                                                {nullptr, 0x1200, 0x200, "fake2", 20}});
-    unwindstack::MapInfo map_info{nullptr, 0x10000, 0x20000, 0, PROT_READ | PROT_EXEC, "/data/fake.so"};
+    unwindstack::MapInfo map_info{nullptr, nullptr, 0x10000, 0x20000, 0,
+                                  PROT_READ | PROT_EXEC, "/data/fake.so"};
     BacktraceUnwindFake(
       std::vector<unwindstack::LocalFrameData>{{&map_info, 0x1a000, 0xa000, "level1", 0},
                                                {&map_info, 0x1b000, 0xb000, "level2", 10}});
diff --git a/libc/platform/bionic/malloc.h b/libc/platform/bionic/malloc.h
index 29e4edc..27d79ff 100644
--- a/libc/platform/bionic/malloc.h
+++ b/libc/platform/bionic/malloc.h
@@ -103,6 +103,9 @@
   // Disable heap tagging. The program must use prctl(PR_SET_TAGGED_ADDR_CTRL) to disable memory tag
   // checks before disabling heap tagging. Heap tagging may not be re-enabled after being disabled.
   M_HEAP_TAGGING_LEVEL_NONE = 0,
+  // Address-only tagging. Heap pointers have a non-zero tag in the most significant byte which is
+  // checked in free(). Memory accesses ignore the tag.
+  M_HEAP_TAGGING_LEVEL_TBI = 1,
 };
 
 // Manipulates bionic-specific handling of memory allocation APIs such as
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 7f1b3d9..8997ceb 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -44,6 +44,7 @@
 struct libc_globals {
   vdso_entry vdso[VDSO_END];
   long setjmp_cookie;
+  uintptr_t heap_pointer_tag;
 
   // In order to allow a complete switch between dispatch tables without
   // the need for copying each function by function in the structure,
diff --git a/libc/upstream-freebsd/lib/libc/stdlib/realpath.c b/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
deleted file mode 100644
index c4bd953..0000000
--- a/libc/upstream-freebsd/lib/libc/stdlib/realpath.c
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. The names of the authors may not be used to endorse or promote
- *    products derived from this software without specific prior written
- *    permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)realpath.c	8.1 (Berkeley) 2/16/94";
-#endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
-#include "namespace.h"
-#include <sys/param.h>
-#include <sys/stat.h>
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include "un-namespace.h"
-
-/*
- * Find the real name of path, by removing all ".", ".." and symlink
- * components.  Returns (resolved) on success, or (NULL) on failure,
- * in which case the path which caused trouble is left in (resolved).
- */
-char *
-realpath(const char * __restrict path, char * __restrict resolved)
-{
-	struct stat sb;
-	char *p, *q, *s;
-	size_t left_len, resolved_len;
-	unsigned symlinks;
-	int m, slen;
-	char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
-
-	if (path == NULL) {
-		errno = EINVAL;
-		return (NULL);
-	}
-	if (path[0] == '\0') {
-		errno = ENOENT;
-		return (NULL);
-	}
-	if (resolved == NULL) {
-		resolved = malloc(PATH_MAX);
-		if (resolved == NULL)
-			return (NULL);
-		m = 1;
-	} else
-		m = 0;
-	symlinks = 0;
-	if (path[0] == '/') {
-		resolved[0] = '/';
-		resolved[1] = '\0';
-		if (path[1] == '\0')
-			return (resolved);
-		resolved_len = 1;
-		left_len = strlcpy(left, path + 1, sizeof(left));
-	} else {
-		if (getcwd(resolved, PATH_MAX) == NULL) {
-			if (m)
-				free(resolved);
-			else {
-				resolved[0] = '.';
-				resolved[1] = '\0';
-			}
-			return (NULL);
-		}
-		resolved_len = strlen(resolved);
-		left_len = strlcpy(left, path, sizeof(left));
-	}
-	if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
-		if (m)
-			free(resolved);
-		errno = ENAMETOOLONG;
-		return (NULL);
-	}
-
-	/*
-	 * Iterate over path components in `left'.
-	 */
-	while (left_len != 0) {
-		/*
-		 * Extract the next path component and adjust `left'
-		 * and its length.
-		 */
-		p = strchr(left, '/');
-		s = p ? p : left + left_len;
-		if (s - left >= sizeof(next_token)) {
-			if (m)
-				free(resolved);
-			errno = ENAMETOOLONG;
-			return (NULL);
-		}
-		memcpy(next_token, left, s - left);
-		next_token[s - left] = '\0';
-		left_len -= s - left;
-		if (p != NULL)
-			memmove(left, s + 1, left_len + 1);
-		if (resolved[resolved_len - 1] != '/') {
-			if (resolved_len + 1 >= PATH_MAX) {
-				if (m)
-					free(resolved);
-				errno = ENAMETOOLONG;
-				return (NULL);
-			}
-			resolved[resolved_len++] = '/';
-			resolved[resolved_len] = '\0';
-		}
-		if (next_token[0] == '\0') {
-			/* Handle consequential slashes. */
-			continue;
-		}
-		else if (strcmp(next_token, ".") == 0)
-			continue;
-		else if (strcmp(next_token, "..") == 0) {
-			/*
-			 * Strip the last path component except when we have
-			 * single "/"
-			 */
-			if (resolved_len > 1) {
-				resolved[resolved_len - 1] = '\0';
-				q = strrchr(resolved, '/') + 1;
-				*q = '\0';
-				resolved_len = q - resolved;
-			}
-			continue;
-		}
-
-		/*
-		 * Append the next path component and lstat() it.
-		 */
-		resolved_len = strlcat(resolved, next_token, PATH_MAX);
-		if (resolved_len >= PATH_MAX) {
-			if (m)
-				free(resolved);
-			errno = ENAMETOOLONG;
-			return (NULL);
-		}
-		if (lstat(resolved, &sb) != 0) {
-			if (m)
-				free(resolved);
-			return (NULL);
-		}
-		if (S_ISLNK(sb.st_mode)) {
-			if (symlinks++ > MAXSYMLINKS) {
-				if (m)
-					free(resolved);
-				errno = ELOOP;
-				return (NULL);
-			}
-			slen = readlink(resolved, symlink, sizeof(symlink) - 1);
-			if (slen < 0) {
-				if (m)
-					free(resolved);
-				return (NULL);
-			}
-			symlink[slen] = '\0';
-			if (symlink[0] == '/') {
-				resolved[1] = 0;
-				resolved_len = 1;
-			} else if (resolved_len > 1) {
-				/* Strip the last path component. */
-				resolved[resolved_len - 1] = '\0';
-				q = strrchr(resolved, '/') + 1;
-				*q = '\0';
-				resolved_len = q - resolved;
-			}
-
-			/*
-			 * If there are any path components left, then
-			 * append them to symlink. The result is placed
-			 * in `left'.
-			 */
-			if (p != NULL) {
-				if (symlink[slen - 1] != '/') {
-					if (slen + 1 >= sizeof(symlink)) {
-						if (m)
-							free(resolved);
-						errno = ENAMETOOLONG;
-						return (NULL);
-					}
-					symlink[slen] = '/';
-					symlink[slen + 1] = 0;
-				}
-				left_len = strlcat(symlink, left,
-				    sizeof(symlink));
-				if (left_len >= sizeof(left)) {
-					if (m)
-						free(resolved);
-					errno = ENAMETOOLONG;
-					return (NULL);
-				}
-			}
-			left_len = strlcpy(left, symlink, sizeof(left));
-		} else if (!S_ISDIR(sb.st_mode) && p != NULL) {
-			if (m)
-				free(resolved);
-			errno = ENOTDIR;
-			return (NULL);
-		}
-	}
-
-	/*
-	 * Remove trailing slash except when the resolved pathname
-	 * is a single "/".
-	 */
-	if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
-		resolved[resolved_len - 1] = '\0';
-	return (resolved);
-}
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 9daa9c4..59f1937 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -4,6 +4,7 @@
 cc_library_static {
     name: "libdl_static",
     defaults: ["linux_bionic_supported"],
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
@@ -32,6 +33,7 @@
 
 cc_library {
     name: "libdl",
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
     static_ndk_lib: true,
@@ -122,6 +124,7 @@
     name: "libdl_android",
 
     defaults: ["linux_bionic_supported"],
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
diff --git a/libm/Android.bp b/libm/Android.bp
index 801129a..d4cdd7b 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -6,6 +6,7 @@
 cc_library {
     name: "libm",
     defaults: ["linux_bionic_supported"],
+    ramdisk_available: true,
     recovery_available: true,
     static_ndk_lib: true,
 
diff --git a/linker/Android.bp b/linker/Android.bp
index acdf094..e21ee60 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -425,6 +425,7 @@
 
     name: "ld-android",
     defaults: ["linux_bionic_supported", "linker_version_script_overlay"],
+    ramdisk_available: true,
     recovery_available: true,
     native_bridge_supported: true,
 
diff --git a/linker/arch/arm_neon/linker_gnu_hash_neon.cpp b/linker/arch/arm_neon/linker_gnu_hash_neon.cpp
index f4127ce..11cf5a3 100644
--- a/linker/arch/arm_neon/linker_gnu_hash_neon.cpp
+++ b/linker/arch/arm_neon/linker_gnu_hash_neon.cpp
@@ -156,7 +156,7 @@
   // Reverse the is-NUL vector so we can use clz to count the number of remaining bytes.
   is_nul = vrev64_u8(is_nul);
   const uint64_t is_nul_u64 = vget_lane_u64(vreinterpret_u64_u8(is_nul), 0);
-  const uint32_t num_valid_bits = is_nul_u64 == 0 ? 64 : __builtin_clzll(is_nul_u64);
+  const uint32_t num_valid_bits = __builtin_clzll(is_nul_u64);
 
   const uint32_t name_len = reinterpret_cast<const char*>(chunk_ptr) - name + (num_valid_bits >> 3);
 
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/dl_test.cpp b/tests/dl_test.cpp
index 05bba05..2b963c7 100644
--- a/tests/dl_test.cpp
+++ b/tests/dl_test.cpp
@@ -135,7 +135,7 @@
   std::string expected_output =
       "ctor: argc=1 argv[0]=" + helper + "\n" +
       "main: argc=1 argv[0]=" + helper + "\n" +
-      "__progname=" + helper + "\n" +
+      "__progname=exec_linker_helper\n" +
       "helper_func called\n";
   ExecTestHelper eth;
   eth.SetArgs({ path_to_linker, helper.c_str(), nullptr });
@@ -151,7 +151,7 @@
   std::string expected_output =
       "ctor: argc=1 argv[0]=" + helper + "\n" +
       "main: argc=1 argv[0]=" + helper + "\n" +
-      "__progname=" + helper + "\n" +
+      "__progname=exec_linker_helper\n" +
       "helper_func called\n";
   ExecTestHelper eth;
   eth.SetArgs({ path_to_linker, helper.c_str(), nullptr });
diff --git a/tests/fenv_test.cpp b/tests/fenv_test.cpp
index d27808f..e983a1c 100644
--- a/tests/fenv_test.cpp
+++ b/tests/fenv_test.cpp
@@ -27,7 +27,7 @@
   volatile float m = 0x1.0p23f;
   volatile float x = f + m;
   ASSERT_FLOAT_EQ(expectation1, x);
-  x -= m;
+  x = x - m;
   ASSERT_EQ(expectation2, x);
 }
 
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index ff4cb71..3f1ec86 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -29,6 +29,7 @@
 #include <limits>
 #include <string>
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <gtest/gtest.h>
 
@@ -36,6 +37,8 @@
 #include "math_data_test.h"
 #include "utils.h"
 
+using namespace std::string_literals;
+
 template <typename T = int (*)(char*)>
 class GenericTemporaryFile {
  public:
@@ -322,6 +325,17 @@
   ASSERT_EQ(ENOENT, errno);
 }
 
+TEST(stdlib, realpath__ELOOP) {
+  TemporaryDir td;
+  std::string link = std::string(td.path) + "/loop";
+  ASSERT_EQ(0, symlink(link.c_str(), link.c_str()));
+
+  errno = 0;
+  char* p = realpath(link.c_str(), nullptr);
+  ASSERT_TRUE(p == nullptr);
+  ASSERT_EQ(ELOOP, errno);
+}
+
 TEST(stdlib, realpath__component_after_non_directory) {
   errno = 0;
   char* p = realpath("/dev/null/.", nullptr);
@@ -350,6 +364,47 @@
   free(p);
 }
 
+TEST(stdlib, realpath__dot) {
+  char* p = realpath("/proc/./version", nullptr);
+  ASSERT_STREQ("/proc/version", p);
+  free(p);
+}
+
+TEST(stdlib, realpath__dot_dot) {
+  char* p = realpath("/dev/../proc/version", nullptr);
+  ASSERT_STREQ("/proc/version", p);
+  free(p);
+}
+
+TEST(stdlib, realpath__deleted) {
+  TemporaryDir td;
+
+  // Create a file "A".
+  std::string A_path = td.path + "/A"s;
+  ASSERT_TRUE(android::base::WriteStringToFile("test\n", A_path));
+
+  // Get an O_PATH fd for it.
+  android::base::unique_fd fd(open(A_path.c_str(), O_PATH));
+  ASSERT_NE(fd, -1);
+
+  // Create a file "A (deleted)".
+  android::base::unique_fd fd2(open((td.path + "/A (deleted)"s).c_str(),
+                                    O_CREAT | O_TRUNC | O_WRONLY, 0644));
+  ASSERT_NE(fd2, -1);
+
+  // Delete "A".
+  ASSERT_EQ(0, unlink(A_path.c_str()));
+
+  // Now realpath() on the O_PATH fd, and check we *don't* get "A (deleted)".
+  std::string path = android::base::StringPrintf("/proc/%d/fd/%d", static_cast<int>(getpid()),
+                                                 fd.get());
+  errno = 0;
+  char* result = realpath(path.c_str(), nullptr);
+  ASSERT_EQ(nullptr, result) << result;
+  ASSERT_EQ(ENOENT, errno);
+  free(result);
+}
+
 TEST(stdlib, qsort) {
   struct s {
     char name[16];
@@ -871,3 +926,24 @@
   ASSERT_TRUE(fabs(expected[1] - load[1]) < 0.5) << expected[1] << ' ' << load[1];
   ASSERT_TRUE(fabs(expected[2] - load[2]) < 0.5) << expected[2] << ' ' << load[2];
 }
+
+TEST(stdlib, getprogname) {
+#if defined(__GLIBC__)
+  GTEST_SKIP() << "glibc doesn't have getprogname()";
+#else
+  // You should always have a name.
+  ASSERT_TRUE(getprogname() != nullptr);
+  // The name should never have a slash in it.
+  ASSERT_TRUE(strchr(getprogname(), '/') == nullptr);
+#endif
+}
+
+TEST(stdlib, setprogname) {
+#if defined(__GLIBC__)
+  GTEST_SKIP() << "glibc doesn't have setprogname()";
+#else
+  // setprogname() only takes the basename of what you give it.
+  setprogname("/usr/bin/muppet");
+  ASSERT_STREQ("muppet", getprogname());
+#endif
+}
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;
 }