Allow init to upgrade MTE to sync.

Bug: 169277947
Test: see other change in topic
Change-Id: I9f5820ffaeb23a4f5539bdbe3db1b455e45d84d8
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();