[compiler-rt] [TSan] Add support for Android (PR #147580)
Fei Peng via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 8 00:57:44 PDT 2025
https://github.com/airpfei updated https://github.com/llvm/llvm-project/pull/147580
>From 98b8be29161c842345fe8ab1271d1833d07c2f79 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
---
.../lib/tsan/rtl/tsan_interceptors_posix.cpp | 8 ++-
.../lib/tsan/rtl/tsan_platform_linux.cpp | 52 +++++++++++++++----
compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp | 6 ++-
3 files changed, 53 insertions(+), 13 deletions(-)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index b46a81031258c..28e35229bd5e4 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -2412,7 +2412,11 @@ TSAN_INTERCEPTOR(int, vfork, int fake) {
}
#endif
-#if SANITIZER_LINUX
+#if SANITIZER_LINUX && !SANITIZER_ANDROID
+// Bionic's pthread_create internally calls clone. When the CLONE_THREAD flag is
+// set, clone does not create a new process but a new thread. This is a
+// workaround for Android. Disabling the interception of clone solves the
+// problem in most scenarios.
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,
@@ -3135,7 +3139,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 4b55aab49a2b9..65d6fcaaa2fa3 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp
@@ -486,8 +486,20 @@ int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
// Reverse operation of libc stack pointer mangling
static uptr UnmangleLongJmpSp(uptr mangled_sp) {
-#if defined(__x86_64__)
-# if SANITIZER_LINUX
+# if SANITIZER_ANDROID
+ if (longjmp_xor_key == 0) {
+ // bionic libc initialization process: __libc_init_globals ->
+ // __libc_init_vdso (calls strcmp) -> __libc_init_setjmp_cookie. strcmp is
+ // intercepted by TSan, so during TSan initialization the setjmp_cookie
+ // remains uninitialized. On Android, longjmp_xor_key must be set on first
+ // use.
+ InitializeLongjmpXorKey();
+ CHECK_NE(longjmp_xor_key, 0);
+ }
+# endif
+
+# if defined(__x86_64__)
+# if SANITIZER_LINUX
// Reverse of:
// xor %fs:0x30, %rsi
// rol $0x11, %rsi
@@ -542,13 +554,13 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) {
# 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_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__)
@@ -556,7 +568,17 @@ static uptr UnmangleLongJmpSp(uptr mangled_sp) {
# else
# define LONG_JMP_SP_ENV_SLOT 6
# endif
-#endif
+# elif SANITIZER_ANDROID
+# ifdef __aarch64__
+# define LONG_JMP_SP_ENV_SLOT 3
+# elif SANITIZER_RISCV64
+# define LONG_JMP_SP_ENV_SLOT 3
+# elif defined(__x86_64__)
+# define LONG_JMP_SP_ENV_SLOT 6
+# else
+# error unsupported
+# endif
+# endif
uptr ExtractLongJmpSp(uptr *env) {
uptr mangled_sp = env[LONG_JMP_SP_ENV_SLOT];
@@ -653,6 +675,16 @@ ThreadState *cur_thread() {
}
CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr));
}
+
+ // https://android.googlesource.com/platform/external/skia/+/refs/tags/android-15.0.0_r36/src/ports/SkMemory_malloc.cpp#105
+ // https://android.googlesource.com/platform/external/scudo/+/refs/tags/android-15.0.0_r36/standalone/tsd_shared.h#198
+ // Workaround to get the correct ThreadState pointer. Scudo allocator uses the
+ // last bit of TLS_SLOT_SANITIZER as a flag of disable memory initialization.
+ // getPlatformAllocatorTlsSlot should not use TLS_SLOT_SANITIZER slot.
+ uptr addr = reinterpret_cast<uptr>(thr);
+ if (addr % 2 != 0) {
+ return reinterpret_cast<ThreadState*>(addr & ~1ULL);
+ }
return thr;
}
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index b1464ccfddeb4..7226962f6fe90 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -206,10 +206,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