Add android_mallopt to query MTE stack state

Bug: 244364391
Change-Id: Ie6267201f0c2e293b27c71cd160a2311c9de8091
diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp
index 9744968..e159fdc 100644
--- a/libc/bionic/malloc_common.cpp
+++ b/libc/bionic/malloc_common.cpp
@@ -333,6 +333,14 @@
 
     return EnableGwpAsan(*reinterpret_cast<android_mallopt_gwp_asan_options_t*>(arg));
   }
+  if (opcode == M_MEMTAG_STACK_IS_ON) {
+    if (arg == nullptr || arg_size != sizeof(bool)) {
+      errno = EINVAL;
+      return false;
+    }
+    *reinterpret_cast<bool*>(arg) = atomic_load(&__libc_globals->memtag_stack);
+    return true;
+  }
   errno = ENOTSUP;
   return false;
 }
diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp
index 6c2f4d9..97e8d15 100644
--- a/libc/bionic/malloc_common_dynamic.cpp
+++ b/libc/bionic/malloc_common_dynamic.cpp
@@ -533,6 +533,14 @@
 
     return EnableGwpAsan(*reinterpret_cast<android_mallopt_gwp_asan_options_t*>(arg));
   }
+  if (opcode == M_MEMTAG_STACK_IS_ON) {
+    if (arg == nullptr || arg_size != sizeof(bool)) {
+      errno = EINVAL;
+      return false;
+    }
+    *reinterpret_cast<bool*>(arg) = atomic_load(&__libc_globals->memtag_stack);
+    return true;
+  }
   // Try heapprofd's mallopt, as it handles options not covered here.
   return HeapprofdMallopt(opcode, arg, arg_size);
 }
diff --git a/libc/platform/bionic/malloc.h b/libc/platform/bionic/malloc.h
index b0c7071..ecc8743 100644
--- a/libc/platform/bionic/malloc.h
+++ b/libc/platform/bionic/malloc.h
@@ -100,6 +100,9 @@
   //   arg_size = sizeof(android_mallopt_gwp_asan_options_t)
   M_INITIALIZE_GWP_ASAN = 10,
 #define M_INITIALIZE_GWP_ASAN M_INITIALIZE_GWP_ASAN
+  // Query whether memtag stack is enabled for this process.
+  M_MEMTAG_STACK_IS_ON = 11,
+#define M_MEMTAG_STACK_IS_ON M_MEMTAG_STACK_IS_ON
 };
 
 typedef struct {
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 5edf97c..66a902e 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -1663,6 +1663,7 @@
        memtag_heap: true,
      },
    },
+   header_libs: ["bionic_libc_platform_headers"],
 }
 
 cc_test {
@@ -1677,6 +1678,7 @@
        memtag_heap: true,
      },
    },
+   header_libs: ["bionic_libc_platform_headers"],
 }
 
 cc_genrule {
diff --git a/tests/libs/stack_tagging_helper.cpp b/tests/libs/stack_tagging_helper.cpp
index f6739b6..a239dc1 100644
--- a/tests/libs/stack_tagging_helper.cpp
+++ b/tests/libs/stack_tagging_helper.cpp
@@ -26,6 +26,8 @@
 #include <unistd.h>
 #include <thread>
 
+#include <bionic/malloc.h>
+
 #include "libs_utils.h"
 
 #if defined(__aarch64__)
@@ -252,6 +254,12 @@
   t.join();
 }
 
+void test_android_mallopt() {
+  bool memtag_stack;
+  CHECK(android_mallopt(M_MEMTAG_STACK_IS_ON, &memtag_stack, sizeof(memtag_stack)));
+  CHECK(memtag_stack);
+}
+
 int main(int argc, char** argv) {
   if (argc < 2) {
     printf("nothing to do\n");
@@ -283,6 +291,11 @@
     return 0;
   }
 
+  if (strcmp(argv[1], "android_mallopt") == 0) {
+    test_android_mallopt();
+    return 0;
+  }
+
   printf("unrecognized command: %s\n", argv[1]);
   return 1;
 }
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index 69f8506..3cc5bbd 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -1383,6 +1383,15 @@
 #endif
 }
 
+TEST(android_mallopt, memtag_stack_is_on) {
+#if defined(__BIONIC__)
+  bool memtag_stack;
+  EXPECT_TRUE(android_mallopt(M_MEMTAG_STACK_IS_ON, &memtag_stack, sizeof(memtag_stack)));
+#else
+  GTEST_SKIP() << "bionic extension";
+#endif
+}
+
 void TestHeapZeroing(int num_iterations, int (*get_alloc_size)(int iteration)) {
   std::vector<void*> allocs;
   constexpr int kMaxBytesToCheckZero = 64;
diff --git a/tests/memtag_stack_test.cpp b/tests/memtag_stack_test.cpp
index 9ff8899..1b336bc 100644
--- a/tests/memtag_stack_test.cpp
+++ b/tests/memtag_stack_test.cpp
@@ -47,7 +47,7 @@
 INSTANTIATE_TEST_SUITE_P(, MemtagStackTest,
                          testing::Combine(testing::Values("vfork_execve", "vfork_execl",
                                                           "vfork_exit", "longjmp",
-                                                          "longjmp_sigaltstack"),
+                                                          "longjmp_sigaltstack", "android_mallopt"),
                                           testing::Bool()),
                          [](const ::testing::TestParamInfo<MemtagStackTest::ParamType>& info) {
                            std::string s = std::get<0>(info.param);