Add an API for per-process disabling memory initialization.
Introduce an android_mallopt(M_DISABLE_MEMORY_MITIGATIONS) API call
that may be used to disable zero- or pattern-init on non-MTE hardware,
or memory tagging on MTE hardware. The intent is that this function
may be called at any time, including when there are multiple threads
running.
Disabling zero- or pattern-init is quite trivial, we just need to set
a global variable to 0 via a Scudo API call (although there will be
some separate work required on the Scudo side to make this operation
thread-safe).
It is a bit more tricky to disable MTE across a process, because
the kernel does not provide an API for disabling tag checking in all
threads in a process, only per-thread. We need to send a signal to each
of the process's threads with a handler that issues the required prctl
call, and lock thread creation for the duration of the API call to
avoid races between thread enumeration and calls to pthread_create().
Bug: 135772972
Change-Id: I81ece86ace916eb6b435ab516cd431ec4b48a3bf
diff --git a/tests/malloc_test.cpp b/tests/malloc_test.cpp
index d692cf9..4ea6d2b 100644
--- a/tests/malloc_test.cpp
+++ b/tests/malloc_test.cpp
@@ -20,6 +20,7 @@
#include <limits.h>
#include <malloc.h>
#include <pthread.h>
+#include <semaphore.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
@@ -45,6 +46,7 @@
#include "SignalUtils.h"
#include "platform/bionic/malloc.h"
+#include "platform/bionic/mte.h"
#include "platform/bionic/mte_kernel.h"
#include "platform/bionic/reserved_signals.h"
#include "private/bionic_config.h"
@@ -1259,3 +1261,39 @@
GTEST_SKIP() << "bionic extension";
#endif
}
+
+TEST(android_mallopt, disable_memory_mitigations) {
+#if defined(__BIONIC__)
+ if (!mte_supported()) {
+ GTEST_SKIP() << "This function can only be tested with MTE";
+ }
+
+#ifdef ANDROID_EXPERIMENTAL_MTE
+ sem_t sem;
+ ASSERT_EQ(0, sem_init(&sem, 0, 0));
+
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(
+ &thread, nullptr,
+ [](void* ptr) -> void* {
+ auto* sem = reinterpret_cast<sem_t*>(ptr);
+ sem_wait(sem);
+ return reinterpret_cast<void*>(prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0));
+ },
+ &sem));
+
+ ASSERT_TRUE(android_mallopt(M_DISABLE_MEMORY_MITIGATIONS, nullptr, 0));
+ ASSERT_EQ(0, sem_post(&sem));
+
+ int my_tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ ASSERT_EQ(PR_MTE_TCF_NONE, my_tagged_addr_ctrl & PR_MTE_TCF_MASK);
+
+ void* retval;
+ ASSERT_EQ(0, pthread_join(thread, &retval));
+ int thread_tagged_addr_ctrl = reinterpret_cast<uintptr_t>(retval);
+ ASSERT_EQ(my_tagged_addr_ctrl, thread_tagged_addr_ctrl);
+#endif
+#else
+ GTEST_SKIP() << "bionic extension";
+#endif
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index 851b86f..d9ad3cc 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -2975,3 +2975,48 @@
spin_helper.UnSpin();
ASSERT_EQ(0, pthread_join(t, nullptr));
}
+
+extern "C" bool android_run_on_all_threads(bool (*func)(void*), void* arg);
+
+TEST(pthread, run_on_all_threads) {
+#if defined(__BIONIC__)
+ pthread_t t;
+ ASSERT_EQ(
+ 0, pthread_create(
+ &t, nullptr,
+ [](void*) -> void* {
+ pthread_attr_t detached;
+ if (pthread_attr_init(&detached) != 0 ||
+ pthread_attr_setdetachstate(&detached, PTHREAD_CREATE_DETACHED) != 0) {
+ return reinterpret_cast<void*>(errno);
+ }
+
+ for (int i = 0; i != 1000; ++i) {
+ pthread_t t1, t2;
+ if (pthread_create(
+ &t1, &detached, [](void*) -> void* { return nullptr; }, nullptr) != 0 ||
+ pthread_create(
+ &t2, nullptr, [](void*) -> void* { return nullptr; }, nullptr) != 0 ||
+ pthread_join(t2, nullptr) != 0) {
+ return reinterpret_cast<void*>(errno);
+ }
+ }
+
+ if (pthread_attr_destroy(&detached) != 0) {
+ return reinterpret_cast<void*>(errno);
+ }
+ return nullptr;
+ },
+ nullptr));
+
+ for (int i = 0; i != 1000; ++i) {
+ ASSERT_TRUE(android_run_on_all_threads([](void* arg) { return arg == nullptr; }, nullptr));
+ }
+
+ void *retval;
+ ASSERT_EQ(0, pthread_join(t, &retval));
+ ASSERT_EQ(nullptr, retval);
+#else
+ GTEST_SKIP() << "bionic-only test";
+#endif
+}
diff --git a/tests/struct_layout_test.cpp b/tests/struct_layout_test.cpp
index 9da702c..00fd4d5 100644
--- a/tests/struct_layout_test.cpp
+++ b/tests/struct_layout_test.cpp
@@ -30,7 +30,7 @@
#define CHECK_OFFSET(name, field, offset) \
check_offset(#name, #field, offsetof(name, field), offset);
#ifdef __LP64__
- CHECK_SIZE(pthread_internal_t, 768);
+ CHECK_SIZE(pthread_internal_t, 776);
CHECK_OFFSET(pthread_internal_t, next, 0);
CHECK_OFFSET(pthread_internal_t, prev, 8);
CHECK_OFFSET(pthread_internal_t, tid, 16);
@@ -44,17 +44,17 @@
CHECK_OFFSET(pthread_internal_t, alternate_signal_stack, 128);
CHECK_OFFSET(pthread_internal_t, shadow_call_stack_guard_region, 136);
CHECK_OFFSET(pthread_internal_t, stack_top, 144);
- CHECK_OFFSET(pthread_internal_t, startup_handshake_lock, 152);
- CHECK_OFFSET(pthread_internal_t, mmap_base, 160);
- CHECK_OFFSET(pthread_internal_t, mmap_size, 168);
- CHECK_OFFSET(pthread_internal_t, mmap_base_unguarded, 176);
- CHECK_OFFSET(pthread_internal_t, mmap_size_unguarded, 184);
- CHECK_OFFSET(pthread_internal_t, vma_name_buffer, 192);
- CHECK_OFFSET(pthread_internal_t, thread_local_dtors, 224);
- CHECK_OFFSET(pthread_internal_t, current_dlerror, 232);
- CHECK_OFFSET(pthread_internal_t, dlerror_buffer, 240);
- CHECK_OFFSET(pthread_internal_t, bionic_tls, 752);
- CHECK_OFFSET(pthread_internal_t, errno_value, 760);
+ CHECK_OFFSET(pthread_internal_t, startup_handshake_lock, 156);
+ CHECK_OFFSET(pthread_internal_t, mmap_base, 168);
+ CHECK_OFFSET(pthread_internal_t, mmap_size, 176);
+ CHECK_OFFSET(pthread_internal_t, mmap_base_unguarded, 184);
+ CHECK_OFFSET(pthread_internal_t, mmap_size_unguarded, 192);
+ CHECK_OFFSET(pthread_internal_t, vma_name_buffer, 200);
+ CHECK_OFFSET(pthread_internal_t, thread_local_dtors, 232);
+ CHECK_OFFSET(pthread_internal_t, current_dlerror, 240);
+ CHECK_OFFSET(pthread_internal_t, dlerror_buffer, 248);
+ CHECK_OFFSET(pthread_internal_t, bionic_tls, 760);
+ CHECK_OFFSET(pthread_internal_t, errno_value, 768);
CHECK_SIZE(bionic_tls, 12200);
CHECK_OFFSET(bionic_tls, key_data, 0);
CHECK_OFFSET(bionic_tls, locale, 2080);
@@ -71,7 +71,7 @@
CHECK_OFFSET(bionic_tls, fdtrack_disabled, 12192);
CHECK_OFFSET(bionic_tls, padding, 12193);
#else
- CHECK_SIZE(pthread_internal_t, 664);
+ CHECK_SIZE(pthread_internal_t, 668);
CHECK_OFFSET(pthread_internal_t, next, 0);
CHECK_OFFSET(pthread_internal_t, prev, 4);
CHECK_OFFSET(pthread_internal_t, tid, 8);
@@ -85,17 +85,17 @@
CHECK_OFFSET(pthread_internal_t, alternate_signal_stack, 68);
CHECK_OFFSET(pthread_internal_t, shadow_call_stack_guard_region, 72);
CHECK_OFFSET(pthread_internal_t, stack_top, 76);
- CHECK_OFFSET(pthread_internal_t, startup_handshake_lock, 80);
- CHECK_OFFSET(pthread_internal_t, mmap_base, 88);
- CHECK_OFFSET(pthread_internal_t, mmap_size, 92);
- CHECK_OFFSET(pthread_internal_t, mmap_base_unguarded, 96);
- CHECK_OFFSET(pthread_internal_t, mmap_size_unguarded, 100);
- CHECK_OFFSET(pthread_internal_t, vma_name_buffer, 104);
- CHECK_OFFSET(pthread_internal_t, thread_local_dtors, 136);
- CHECK_OFFSET(pthread_internal_t, current_dlerror, 140);
- CHECK_OFFSET(pthread_internal_t, dlerror_buffer, 144);
- CHECK_OFFSET(pthread_internal_t, bionic_tls, 656);
- CHECK_OFFSET(pthread_internal_t, errno_value, 660);
+ CHECK_OFFSET(pthread_internal_t, startup_handshake_lock, 84);
+ CHECK_OFFSET(pthread_internal_t, mmap_base, 92);
+ CHECK_OFFSET(pthread_internal_t, mmap_size, 96);
+ CHECK_OFFSET(pthread_internal_t, mmap_base_unguarded, 100);
+ CHECK_OFFSET(pthread_internal_t, mmap_size_unguarded, 104);
+ CHECK_OFFSET(pthread_internal_t, vma_name_buffer, 108);
+ CHECK_OFFSET(pthread_internal_t, thread_local_dtors, 140);
+ CHECK_OFFSET(pthread_internal_t, current_dlerror, 144);
+ CHECK_OFFSET(pthread_internal_t, dlerror_buffer, 148);
+ CHECK_OFFSET(pthread_internal_t, bionic_tls, 660);
+ CHECK_OFFSET(pthread_internal_t, errno_value, 664);
CHECK_SIZE(bionic_tls, 11080);
CHECK_OFFSET(bionic_tls, key_data, 0);
CHECK_OFFSET(bionic_tls, locale, 1040);