From 78114a2d7aac74315a5c7d48135c9e1f9196315e Mon Sep 17 00:00:00 2001 From: Fei Peng 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 | 89 +++++++++++-------- compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp | 6 +- 4 files changed, 73 insertions(+), 42 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..395682a4ec8db 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_platform_linux.cpp @@ -67,7 +67,7 @@ void *__libc_stack_end = 0; #endif #if SANITIZER_LINUX && (defined(__aarch64__) || defined(__loongarch_lp64)) && \ - !SANITIZER_GO + !SANITIZER_GO && !SANITIZER_ANDROID # define INIT_LONGJMP_XOR_KEY 1 #else # define INIT_LONGJMP_XOR_KEY 0 @@ -415,7 +415,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,9 +484,55 @@ 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__) +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 @@ -528,41 +574,6 @@ 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 // 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 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(