Reland^2 "[MTE] remap stacks with PROT_MTE when requested by dlopened library"
Also enable stack MTE if main binary links in a library that needs it.
Otherwise the following is possible:
1. a binary doesn't require stack MTE, but links in libraries that use
stg on the stack
2. that binary later dlopens a library that requires stack MTE, and our
logic in dlopen remaps the stacks with MTE
3. the libraries from step 1 now have tagged pointers with missing tags
in memory, so things go wrong
This reverts commit f53e91cc810be2a36377f3b7765f50c89f1f0046.
Reason for revert: Fixed problem detected in b/324568991
Test: atest memtag_stack_dlopen_test with MTE enabled
Test: check crash is gone on fullmte build
Change-Id: I4a93f6814a19683c3ea5fe1e6d455df5459d31e1
diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp
index 6a7ee2f..bfe2f98 100644
--- a/libc/bionic/pthread_internal.cpp
+++ b/libc/bionic/pthread_internal.cpp
@@ -40,6 +40,7 @@
#include "private/ErrnoRestorer.h"
#include "private/ScopedRWLock.h"
#include "private/bionic_futex.h"
+#include "private/bionic_globals.h"
#include "private/bionic_tls.h"
static pthread_internal_t* g_thread_list = nullptr;
@@ -119,6 +120,89 @@
return nullptr;
}
+static uintptr_t __get_main_stack_startstack() {
+ FILE* fp = fopen("/proc/self/stat", "re");
+ if (fp == nullptr) {
+ async_safe_fatal("couldn't open /proc/self/stat: %m");
+ }
+
+ char line[BUFSIZ];
+ if (fgets(line, sizeof(line), fp) == nullptr) {
+ async_safe_fatal("couldn't read /proc/self/stat: %m");
+ }
+
+ fclose(fp);
+
+ // See man 5 proc. There's no reason comm can't contain ' ' or ')',
+ // so we search backwards for the end of it. We're looking for this field:
+ //
+ // startstack %lu (28) The address of the start (i.e., bottom) of the stack.
+ uintptr_t startstack = 0;
+ const char* end_of_comm = strrchr(line, ')');
+ if (sscanf(end_of_comm + 1,
+ " %*c "
+ "%*d %*d %*d %*d %*d "
+ "%*u %*u %*u %*u %*u %*u %*u "
+ "%*d %*d %*d %*d %*d %*d "
+ "%*u %*u %*d %*u %*u %*u %" SCNuPTR,
+ &startstack) != 1) {
+ async_safe_fatal("couldn't parse /proc/self/stat");
+ }
+
+ return startstack;
+}
+
+void __find_main_stack_limits(uintptr_t* low, uintptr_t* high) {
+ // Ask the kernel where our main thread's stack started.
+ uintptr_t startstack = __get_main_stack_startstack();
+
+ // Hunt for the region that contains that address.
+ FILE* fp = fopen("/proc/self/maps", "re");
+ if (fp == nullptr) {
+ async_safe_fatal("couldn't open /proc/self/maps: %m");
+ }
+ char line[BUFSIZ];
+ while (fgets(line, sizeof(line), fp) != nullptr) {
+ uintptr_t lo, hi;
+ if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR, &lo, &hi) == 2) {
+ if (lo <= startstack && startstack <= hi) {
+ *low = lo;
+ *high = hi;
+ fclose(fp);
+ return;
+ }
+ }
+ }
+ async_safe_fatal("stack not found in /proc/self/maps");
+}
+
+void __pthread_internal_remap_stack_with_mte() {
+#if defined(__aarch64__)
+ // If process doesn't have MTE enabled, we don't need to do anything.
+ if (!__libc_globals->memtag) return;
+ bool prev = true;
+ __libc_globals.mutate(
+ [&prev](libc_globals* globals) { prev = atomic_exchange(&globals->memtag_stack, true); });
+ if (prev) return;
+ uintptr_t lo, hi;
+ __find_main_stack_limits(&lo, &hi);
+
+ if (mprotect(reinterpret_cast<void*>(lo), hi - lo,
+ PROT_READ | PROT_WRITE | PROT_MTE | PROT_GROWSDOWN)) {
+ async_safe_fatal("error: failed to set PROT_MTE on main thread");
+ }
+ ScopedWriteLock creation_locker(&g_thread_creation_lock);
+ ScopedReadLock list_locker(&g_thread_list_lock);
+ for (pthread_internal_t* t = g_thread_list; t != nullptr; t = t->next) {
+ if (t->terminating || t->is_main()) continue;
+ if (mprotect(t->mmap_base_unguarded, t->mmap_size_unguarded,
+ PROT_READ | PROT_WRITE | PROT_MTE)) {
+ async_safe_fatal("error: failed to set PROT_MTE on thread: %d", t->tid);
+ }
+ }
+#endif
+}
+
bool android_run_on_all_threads(bool (*func)(void*), void* arg) {
// Take the locks in this order to avoid inversion (pthread_create ->
// __pthread_internal_add).