[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