Allow init to upgrade MTE to sync.

Bug: 169277947
Test: see other change in topic
Change-Id: I9f5820ffaeb23a4f5539bdbe3db1b455e45d84d8
diff --git a/libc/bionic/heap_tagging.h b/libc/bionic/heap_tagging.h
index a3588b8..5bc1da0 100644
--- a/libc/bionic/heap_tagging.h
+++ b/libc/bionic/heap_tagging.h
@@ -43,3 +43,19 @@
 // This function can be called in a multithreaded context, and thus should
 // only be called when holding the `g_heap_tagging_lock`.
 bool SetHeapTaggingLevel(HeapTaggingLevel level);
+
+// This is static because libc_nomalloc uses this but does not need to link the
+// cpp file.
+__attribute__((unused)) static inline const char* DescribeTaggingLevel(
+    HeapTaggingLevel level) {
+  switch (level) {
+    case M_HEAP_TAGGING_LEVEL_NONE:
+      return "none";
+    case M_HEAP_TAGGING_LEVEL_TBI:
+      return "tbi";
+    case M_HEAP_TAGGING_LEVEL_ASYNC:
+      return "async";
+    case M_HEAP_TAGGING_LEVEL_SYNC:
+      return "sync";
+  }
+}
diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp
index 8084e73..5d5ecac 100644
--- a/libc/bionic/libc_init_common.cpp
+++ b/libc/bionic/libc_init_common.cpp
@@ -27,11 +27,12 @@
  */
 
 #include "libc_init_common.h"
-#include "heap_tagging.h"
 
+#include <async_safe/log.h>
 #include <elf.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -42,8 +43,8 @@
 #include <sys/time.h>
 #include <unistd.h>
 
-#include <async_safe/log.h>
-
+#include "heap_tagging.h"
+#include "private/ScopedPthreadMutexLocker.h"
 #include "private/WriteProtected.h"
 #include "private/bionic_defs.h"
 #include "private/bionic_globals.h"
@@ -104,6 +105,51 @@
 }
 
 __BIONIC_WEAK_FOR_NATIVE_BRIDGE
+__attribute__((no_sanitize("hwaddress", "memtag"))) void
+__libc_init_mte_late() {
+#if defined(__aarch64__)
+  if (!__libc_shared_globals()->heap_tagging_upgrade_timer_sec) {
+    return;
+  }
+  struct sigevent event = {};
+  static timer_t timer;
+  event.sigev_notify = SIGEV_THREAD;
+  event.sigev_notify_function = [](union sigval) {
+    async_safe_format_log(ANDROID_LOG_INFO, "libc",
+                          "Downgrading MTE to async.");
+    ScopedPthreadMutexLocker l(&g_heap_tagging_lock);
+    SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC);
+    timer_delete(timer);
+  };
+
+  if (timer_create(CLOCK_REALTIME, &event, &timer) == -1) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "Failed to create MTE downgrade timer: %m");
+    // Revert back to ASYNC. If we fail to create or arm the timer, otherwise
+    // the process would be indefinitely stuck in SYNC.
+    SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC);
+    return;
+  }
+
+  struct itimerspec timerspec = {};
+  timerspec.it_value.tv_sec =
+      __libc_shared_globals()->heap_tagging_upgrade_timer_sec;
+  if (timer_settime(timer, /* flags= */ 0, &timerspec, nullptr) == -1) {
+    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                          "Failed to arm MTE downgrade timer: %m");
+    // Revert back to ASYNC. If we fail to create or arm the timer, otherwise
+    // the process would be indefinitely stuck in SYNC.
+    SetHeapTaggingLevel(M_HEAP_TAGGING_LEVEL_ASYNC);
+    timer_delete(timer);
+    return;
+  }
+  async_safe_format_log(
+      ANDROID_LOG_INFO, "libc", "Armed MTE downgrade timer for %" PRId64 " s",
+      __libc_shared_globals()->heap_tagging_upgrade_timer_sec);
+#endif
+}
+
+__BIONIC_WEAK_FOR_NATIVE_BRIDGE
 void __libc_add_main_thread() {
   // Get the main thread from TLS and add it to the thread list.
   pthread_internal_t* main_thread = __get_thread();
diff --git a/libc/bionic/libc_init_common.h b/libc/bionic/libc_init_common.h
index 15c747e..6b39d6d 100644
--- a/libc/bionic/libc_init_common.h
+++ b/libc/bionic/libc_init_common.h
@@ -60,6 +60,8 @@
 
 __LIBC_HIDDEN__ void __libc_init_scudo();
 
+__LIBC_HIDDEN__ void __libc_init_mte_late();
+
 __LIBC_HIDDEN__ void __libc_init_AT_SECURE(char** envp);
 
 // The fork handler must be initialised after __libc_init_malloc, as
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 24efbf5..c61810e 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -154,6 +154,8 @@
     __cxa_atexit(__libc_fini,structors->fini_array,nullptr);
   }
 
+  __libc_init_mte_late();
+
   exit(slingshot(args.argc - __libc_shared_globals()->initial_linker_arg_count,
                  args.argv + __libc_shared_globals()->initial_linker_arg_count,
                  args.envp));
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 79a4019..46eafc6 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -29,6 +29,7 @@
 #include <android/api-level.h>
 #include <elf.h>
 #include <errno.h>
+#include <malloc.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -36,10 +37,9 @@
 #include <sys/auxv.h>
 #include <sys/mman.h>
 
+#include "async_safe/log.h"
+#include "heap_tagging.h"
 #include "libc_init_common.h"
-#include "pthread_internal.h"
-#include "sysprop_helpers.h"
-
 #include "platform/bionic/macros.h"
 #include "platform/bionic/mte.h"
 #include "platform/bionic/page.h"
@@ -51,7 +51,9 @@
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_tls.h"
+#include "pthread_internal.h"
 #include "sys/system_properties.h"
+#include "sysprop_helpers.h"
 
 #if __has_feature(hwaddress_sanitizer)
 #include <sanitizer/hwasan_interface.h>
@@ -302,7 +304,34 @@
                                                                          void* stack_top) {
   bool memtag_stack;
   HeapTaggingLevel level = __get_heap_tagging_level(phdr_start, phdr_ct, load_bias, &memtag_stack);
-
+  char* env = getenv("BIONIC_MEMTAG_UPGRADE_SECS");
+  int64_t timed_upgrade = 0;
+  if (env) {
+    char* endptr;
+    timed_upgrade = strtoll(env, &endptr, 10);
+    if (*endptr != '\0' || timed_upgrade < 0) {
+      async_safe_format_log(ANDROID_LOG_ERROR, "libc",
+                            "Invalid value for BIONIC_MEMTAG_UPGRADE_SECS: %s",
+                            env);
+      timed_upgrade = 0;
+    }
+    // Make sure that this does not get passed to potential processes inheriting
+    // this environment.
+    unsetenv("BIONIC_MEMTAG_UPGRADE_SECS");
+  }
+  if (timed_upgrade) {
+    if (level == M_HEAP_TAGGING_LEVEL_ASYNC) {
+      async_safe_format_log(ANDROID_LOG_INFO, "libc",
+                            "Attempting timed MTE upgrade from async to sync.");
+      __libc_shared_globals()->heap_tagging_upgrade_timer_sec = timed_upgrade;
+      level = M_HEAP_TAGGING_LEVEL_SYNC;
+    } else if (level != M_HEAP_TAGGING_LEVEL_SYNC) {
+      async_safe_format_log(
+          ANDROID_LOG_ERROR, "libc",
+          "Requested timed MTE upgrade from invalid %s to sync. Ignoring.",
+          DescribeTaggingLevel(level));
+    }
+  }
   if (level == M_HEAP_TAGGING_LEVEL_SYNC || level == M_HEAP_TAGGING_LEVEL_ASYNC) {
     unsigned long prctl_arg = PR_TAGGED_ADDR_ENABLE | PR_MTE_TAG_SET_NONZERO;
     prctl_arg |= (level == M_HEAP_TAGGING_LEVEL_SYNC) ? PR_MTE_TCF_SYNC : PR_MTE_TCF_ASYNC;
@@ -333,6 +362,8 @@
   if (prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == 0) {
     __libc_shared_globals()->initial_heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
   }
+  // We did not enable MTE, so we do not need to arm the upgrade timer.
+  __libc_shared_globals()->heap_tagging_upgrade_timer_sec = 0;
 }
 #else   // __aarch64__
 void __libc_init_mte(const void*, size_t, uintptr_t, void*) {}
@@ -384,6 +415,8 @@
     __cxa_atexit(__libc_fini,structors->fini_array,nullptr);
   }
 
+  __libc_init_mte_late();
+
   exit(slingshot(args.argc, args.argv, args.envp));
 }
 
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index 3c86ef5..c7e951d 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -29,19 +29,19 @@
 #ifndef _PRIVATE_BIONIC_GLOBALS_H
 #define _PRIVATE_BIONIC_GLOBALS_H
 
+#include <inttypes.h>
+#include <link.h>
+#include <platform/bionic/malloc.h>
+#include <pthread.h>
 #include <stdatomic.h>
 #include <sys/cdefs.h>
-#include <link.h>
-#include <pthread.h>
 
+#include "private/WriteProtected.h"
 #include "private/bionic_allocator.h"
 #include "private/bionic_elf_tls.h"
 #include "private/bionic_fdsan.h"
 #include "private/bionic_malloc_dispatch.h"
 #include "private/bionic_vdso.h"
-#include "private/WriteProtected.h"
-
-#include <platform/bionic/malloc.h>
 
 struct libc_globals {
   vdso_entry vdso[VDSO_END];
@@ -114,6 +114,7 @@
 
   HeapTaggingLevel initial_heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
   bool initial_memtag_stack = false;
+  int64_t heap_tagging_upgrade_timer_sec = 0;
 };
 
 __LIBC_HIDDEN__ libc_shared_globals* __libc_shared_globals();