Allow the kernel to upgrade ASYNC mode processes to SYNC mode.

On devices where the performance of ASYNC mode is similar to SYNC
mode on certain CPUs, OEMs may choose to configure the kernel to
prefer SYNC mode on those CPUs by writing the value "sync" to the
sysfs node: /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred

The kernel will only respect the per-CPU preference if the user program
allows this by specifying the preferred mode as a member of a set of
allowed modes. Since only kernels with r.android.com/1754670 support
specifying multiple modes, fall back to trying to specify a single
mode if that doesn't work.

Bug: 189966263
Change-Id: Ie7ada3b073178b7967f0819cbdadc2d8e3a2c648
Merged-In: Ie7ada3b073178b7967f0819cbdadc2d8e3a2c648
diff --git a/libc/bionic/heap_tagging.cpp b/libc/bionic/heap_tagging.cpp
index ffbabb9..41aa205 100644
--- a/libc/bionic/heap_tagging.cpp
+++ b/libc/bionic/heap_tagging.cpp
@@ -139,7 +139,12 @@
       }
 
       if (tag_level == M_HEAP_TAGGING_LEVEL_ASYNC) {
-        set_tcf_on_all_threads(PR_MTE_TCF_ASYNC);
+        // When entering ASYNC mode, specify that we want to allow upgrading to SYNC by OR'ing in
+        // the SYNC flag. But if the kernel doesn't support specifying multiple TCF modes, fall back
+        // to specifying a single mode.
+        if (!set_tcf_on_all_threads(PR_MTE_TCF_ASYNC | PR_MTE_TCF_SYNC)) {
+          set_tcf_on_all_threads(PR_MTE_TCF_ASYNC);
+        }
 #if defined(USE_SCUDO)
         scudo_malloc_set_track_allocation_stacks(0);
 #endif
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 069ebb0..3a8513f 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -311,7 +311,11 @@
     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;
 
-    if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg, 0, 0, 0) == 0) {
+    // When entering ASYNC mode, specify that we want to allow upgrading to SYNC by OR'ing in the
+    // SYNC flag. But if the kernel doesn't support specifying multiple TCF modes, fall back to
+    // specifying a single mode.
+    if (prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg | PR_MTE_TCF_SYNC, 0, 0, 0) == 0 ||
+        prctl(PR_SET_TAGGED_ADDR_CTRL, prctl_arg, 0, 0, 0) == 0) {
       __libc_shared_globals()->initial_heap_tagging_level = level;
       return;
     }