[compiler-rt] [TSan] Add support for Android (PR #147580)
Fei Peng via llvm-commits
llvm-commits at lists.llvm.org
Thu Jul 31 11:29:15 PDT 2025
https://github.com/airpfei updated https://github.com/llvm/llvm-project/pull/147580
>From 821afeba591d341156af2f12caffb70bf33612af Mon Sep 17 00:00:00 2001
From: Fei Peng <pengfei.02 at bytedance.com>
Date: Thu, 17 Jul 2025 16:29:56 -0700
Subject: [PATCH] [TSan] Add support for Android
---
.../sanitizer_common_interceptors.inc | 16 ++
.../lib/tsan/rtl/tsan_interceptors_posix.cpp | 4 +-
.../lib/tsan/rtl/tsan_platform_linux.cpp | 172 ++++++++++--------
compiler-rt/lib/tsan/rtl/tsan_rtl.cpp | 3 +
compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp | 6 +-
5 files changed, 122 insertions(+), 79 deletions(-)
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
index 2d6cf7fc3282f..0d7b34bd8ee41 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
@@ -1287,6 +1287,22 @@ INTERCEPTOR(int, puts, char *s) {
#if SANITIZER_INTERCEPT_PRCTL
INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) {
+# if SANITIZER_ANDROID
+ // This is a workaround to avoid the crash by leveraging compiler
+ // optimizations, which convert the code into a tail call so that
+ // no PAC-related instructions are generated.
+ // The root cause of the crash is that PR_PAC_RESET_KEYS generates
+ // a new PAC key. As a result, paciasp and autiasp use different
+ // keys, leading to the crash.
+ // However, this workaround does not prevent the crash in debug
+ // builds, since compiler optimizations are disabled and the
+ // function is not converted into a tail call.
+ static const int PR_PAC_RESET_KEYS = 54;
+ if (option == PR_PAC_RESET_KEYS) {
+ return REAL(prctl)(option, arg2, arg3, arg4, arg5);
+ }
+# endif
+
void *ctx;
COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5);
static const int PR_SET_NAME = 15;
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index 795e05394d71a..e8e10cd698e8f 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -2411,7 +2411,7 @@ TSAN_INTERCEPTOR(int, vfork, int fake) {
}
#endif
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
TSAN_INTERCEPTOR(int, clone, int (*fn)(void *), void *stack, int flags,
void *arg, int *parent_tid, void *tls, pid_t *child_tid) {
SCOPED_INTERCEPTOR_RAW(clone, fn, stack, flags, arg, parent_tid, tls,
@@ -3120,7 +3120,7 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(fork);
TSAN_INTERCEPT(vfork);
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
TSAN_INTERCEPT(clone);
#endif
#if !SANITIZER_ANDROID
diff --git a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
index 2c55645a15479..8945867a5558c 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
@@ -66,15 +66,16 @@ extern "C" void *__libc_stack_end;
void *__libc_stack_end = 0;
#endif
-#if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64)) && \
- !SANITIZER_GO
-# define INIT_LONGJMP_XOR_KEY 1
-#else
-# define INIT_LONGJMP_XOR_KEY 0
-#endif
+# if SANITIZER_LINUX && \
+ (defined(__aarch64__) || defined(__loongarch_lp64)) && !SANITIZER_GO && \
+ !SANITIZER_ANDROID
+# define INIT_LONGJMP_XOR_KEY 1
+# else
+# define INIT_LONGJMP_XOR_KEY 0
+# endif
-#if INIT_LONGJMP_XOR_KEY
-#include "interception/interception.h"
+# if INIT_LONGJMP_XOR_KEY
+# include "interception/interception.h"
// Must be declared outside of other namespaces.
DECLARE_REAL(int, _setjmp, void *env)
#endif
@@ -415,7 +416,7 @@ void InitializePlatform() {
// is not compiled with -pie.
#if !SANITIZER_GO
{
-# if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64))
+# if INIT_LONGJMP_XOR_KEY
// Initialize the xor key used in {sig}{set,long}jump.
InitializeLongjmpXorKey();
# endif
@@ -484,10 +485,56 @@ int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
return res;
}
+# if SANITIZER_NETBSD
+# ifdef __x86_64__
+# define LONG_JMP_SP_ENV_SLOT 6
+# else
+# error unsupported
+# endif
+# elif defined(__powerpc__)
+# define LONG_JMP_SP_ENV_SLOT 0
+# elif SANITIZER_FREEBSD
+# ifdef __aarch64__
+# define LONG_JMP_SP_ENV_SLOT 1
+# else
+# define LONG_JMP_SP_ENV_SLOT 2
+# endif
+# elif SANITIZER_LINUX && !SANITIZER_ANDROID
+# ifdef __aarch64__
+# define LONG_JMP_SP_ENV_SLOT 13
+# elif defined(__loongarch__)
+# define LONG_JMP_SP_ENV_SLOT 1
+# elif defined(__mips64)
+# define LONG_JMP_SP_ENV_SLOT 1
+# elif SANITIZER_RISCV64
+# define LONG_JMP_SP_ENV_SLOT 13
+# elif defined(__s390x__)
+# define LONG_JMP_SP_ENV_SLOT 9
+# else
+# define LONG_JMP_SP_ENV_SLOT 6
+# endif
+# elif SANITIZER_ANDROID
+// https://android.googlesource.com/platform/bionic/+/refs/heads/android16-release/libc/arch-arm64/bionic/setjmp.S
+// https://android.googlesource.com/platform/bionic/+/refs/heads/android16-release/libc/arch-x86_64/bionic/setjmp.S
+// https://android.googlesource.com/platform/bionic/+/refs/heads/android16-release/libc/arch-riscv64/bionic/setjmp.S
+# if defined(__aarch64__) || SANITIZER_RISCV64
+# define LONG_JMP_SP_ENV_SLOT 3
+# define LONG_JMP_COOKIE_ENV_SLOT 0
+# elif defined(__x86_64__)
+# define LONG_JMP_SP_ENV_SLOT 6
+# define LONG_JMP_COOKIE_ENV_SLOT 8
+# else
+# error unsupported
+# endif
+# endif
+
// Reverse operation of libc stack pointer mangling
-static uptr UnmangleLongJmpSp(uptr mangled_sp) {
-#if defined(__x86_64__)
-# if SANITIZER_LINUX
+uptr ExtractLongJmpSp(uptr *env) {
+ uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT];
+# if SANITIZER_ANDROID
+ return mangled_sp ^ (env[LONG_JMP_COOKIE_ENV_SLOT] & ~1ULL);
+# elif defined(__x86_64__)
+# if SANITIZER_LINUX
// Reverse of:
// xor %fs:0x30, %rsi
// rol $0x11, %rsi
@@ -497,25 +544,25 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) {
: "=r" (sp)
: "0" (mangled_sp));
return sp;
-# else
+# else
return mangled_sp;
-# endif
-#elif defined(__aarch64__)
-# if SANITIZER_LINUX
+# endif
+# elif defined(__aarch64__)
+# if SANITIZER_LINUX
return mangled_sp ^ longjmp_xor_key;
-# else
+# else
return mangled_sp;
-# endif
-#elif defined(__loongarch_lp64)
+# endif
+# elif defined(__loongarch_lp64)
return mangled_sp ^ longjmp_xor_key;
-#elif defined(__powerpc64__)
+# elif defined(__powerpc64__)
// Reverse of:
// ld r4, -28696(r13)
// xor r4, r3, r4
uptr xor_key;
asm("ld %0, -28696(%%r13)" : "=r" (xor_key));
return mangled_sp ^ xor_key;
-#elif defined(__mips__)
+# elif defined(__mips__)
return mangled_sp;
# elif SANITIZER_RISCV64
return mangled_sp;
@@ -528,42 +575,7 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) {
# endif
}
-#if SANITIZER_NETBSD
-# ifdef __x86_64__
-# define LONG_JMP_SP_ENV_SLOT 6
-# else
-# error unsupported
-# endif
-#elif defined(__powerpc__)
-# define LONG_JMP_SP_ENV_SLOT 0
-#elif SANITIZER_FREEBSD
-# ifdef __aarch64__
-# define LONG_JMP_SP_ENV_SLOT 1
-# else
-# define LONG_JMP_SP_ENV_SLOT 2
-# endif
-#elif SANITIZER_LINUX
-# ifdef __aarch64__
-# define LONG_JMP_SP_ENV_SLOT 13
-# elif defined(__loongarch__)
-# define LONG_JMP_SP_ENV_SLOT 1
-# elif defined(__mips64)
-# define LONG_JMP_SP_ENV_SLOT 1
-# elif SANITIZER_RISCV64
-# define LONG_JMP_SP_ENV_SLOT 13
-# elif defined(__s390x__)
-# define LONG_JMP_SP_ENV_SLOT 9
-# else
-# define LONG_JMP_SP_ENV_SLOT 6
-# endif
-#endif
-
-uptr ExtractLongJmpSp(uptr *env) {
- uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT];
- return UnmangleLongJmpSp(mangled_sp);
-}
-
-#if INIT_LONGJMP_XOR_KEY
+# if INIT_LONGJMP_XOR_KEY
// GLIBC mangles the function pointers in jmp_buf (used in {set,long}*jmp
// functions) by XORing them with a random key. For AArch64 it is a global
// variable rather than a TCB one (as for x86_64/powerpc). We obtain the key by
@@ -575,17 +587,17 @@ static void InitializeLongjmpXorKey() {
// 2. Retrieve vanilla/mangled SP.
uptr sp;
-#ifdef __loongarch__
+# ifdef __loongarch__
asm("move %0, $sp" : "=r" (sp));
-#else
+# else
asm("mov %0, sp" : "=r" (sp));
-#endif
+# endif
uptr mangled_sp = ((uptr *)&env)[LONG_JMP_SP_ENV_SLOT];
// 3. xor SPs to obtain key.
longjmp_xor_key = mangled_sp ^ sp;
}
-#endif
+# endif
extern "C" void __tsan_tls_initialization() {}
@@ -616,43 +628,51 @@ int call_pthread_cancel_with_cleanup(int (*fn)(void *arg),
pthread_cleanup_pop(0);
return res;
}
-#endif // !SANITIZER_GO
+# endif // !SANITIZER_GO
-#if !SANITIZER_GO
-void ReplaceSystemMalloc() { }
-#endif
+# if !SANITIZER_GO
+void ReplaceSystemMalloc() {}
+# endif
-#if !SANITIZER_GO
-#if SANITIZER_ANDROID
+# if !SANITIZER_GO
+# if SANITIZER_ANDROID
// On Android, one thread can call intercepted functions after
// DestroyThreadState(), so add a fake thread state for "dead" threads.
static ThreadState *dead_thread_state = nullptr;
ThreadState *cur_thread() {
- ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
+ ThreadState *thr = reinterpret_cast<ThreadState *>(*get_android_tls_ptr());
if (thr == nullptr) {
__sanitizer_sigset_t emptyset;
internal_sigfillset(&emptyset);
__sanitizer_sigset_t oldset;
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset));
- thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
+ thr = reinterpret_cast<ThreadState *>(*get_android_tls_ptr());
if (thr == nullptr) {
- thr = reinterpret_cast<ThreadState*>(MmapOrDie(sizeof(ThreadState),
- "ThreadState"));
+ thr = reinterpret_cast<ThreadState *>(
+ MmapOrDie(sizeof(ThreadState), "ThreadState"));
*get_android_tls_ptr() = reinterpret_cast<uptr>(thr);
if (dead_thread_state == nullptr) {
- dead_thread_state = reinterpret_cast<ThreadState*>(
+ dead_thread_state = reinterpret_cast<ThreadState *>(
MmapOrDie(sizeof(ThreadState), "ThreadState"));
dead_thread_state->fast_state.SetIgnoreBit();
dead_thread_state->ignore_interceptors = 1;
dead_thread_state->is_dead = true;
- *const_cast<u32*>(&dead_thread_state->tid) = -1;
+ *const_cast<u32 *>(&dead_thread_state->tid) = -1;
CHECK_EQ(0, internal_mprotect(dead_thread_state, sizeof(ThreadState),
PROT_READ));
}
}
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr));
}
+
+ // This is a temporary workaround.
+ // Somewhere wrote get_android_tls_ptr unexpected.
+ uptr addr = reinterpret_cast<uptr>(thr);
+ if (addr % 2 != 0) {
+ return reinterpret_cast<ThreadState *>(addr & ~1ULL);
+ }
+
return thr;
}
@@ -665,15 +685,15 @@ void cur_thread_finalize() {
internal_sigfillset(&emptyset);
__sanitizer_sigset_t oldset;
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset));
- ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr());
+ ThreadState *thr = reinterpret_cast<ThreadState *>(*get_android_tls_ptr());
if (thr != dead_thread_state) {
*get_android_tls_ptr() = reinterpret_cast<uptr>(dead_thread_state);
UnmapOrDie(thr, sizeof(ThreadState));
}
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr));
}
-#endif // SANITIZER_ANDROID
-#endif // if !SANITIZER_GO
+# endif // SANITIZER_ANDROID
+# endif // if !SANITIZER_GO
} // namespace __tsan
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
index 0d7247a56a4c2..01d17d52e4c5f 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp
@@ -733,6 +733,9 @@ void Initialize(ThreadState *thr) {
#if !SANITIZER_GO
InitializeShadowMemory();
InitializeAllocatorLate();
+#endif
+
+#if !SANITIZER_GO
InstallDeadlySignalHandlers(TsanOnDeadlySignal);
#endif
// Setup correct file descriptor for error reports.
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index c6a8fd2acb6a8..2cf0a90c5a847 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -188,10 +188,14 @@ void ThreadStart(ThreadState *thr, Tid tid, ThreadID os_id,
}
#endif
-#if !SANITIZER_GO
+#if !SANITIZER_GO && !SANITIZER_ANDROID
// Don't imitate stack/TLS writes for the main thread,
// because its initialization is synchronized with all
// subsequent threads anyway.
+ // Because thr is created by MmapOrDie, the thr object
+ // is not in tls, the pointer of thr object is in
+ // TLS_SLOT_SANITIZER slot. So skip this check on
+ // Android platform.
if (tid != kMainTid) {
if (stk_addr && stk_size) {
const uptr pc = StackTrace::GetNextInstructionPc(
More information about the llvm-commits
mailing list