Skip to content

Conversation

MaskRay
Copy link
Member

@MaskRay MaskRay commented Aug 19, 2025

On glibc x86-64, functions like pthread_cond_init exist in two versions:
2.2 (older pthread_cond_t) and 2.3.2 (newer pthread_cond_t). In glibc
versions prior to 2.36, using an unversioned interceptor
(dlsym(RTLD_NEXT, "pthread_cond_init")) retrieves the older 2.2
symbol, which is not desired
(https://sourceware.org/bugzilla/show_bug.cgi?id=14932).

For newer architectures, such as aarch64 (introduced in glibc 2.17) and
riscv64 (introduced in glibc 2.27), a versioned interceptor is
unnecessary.

Created using spr 1.3.5-bogner
@llvmbot
Copy link
Member

llvmbot commented Aug 19, 2025

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: Fangrui Song (MaskRay)

Changes

On glibc x86-64, functions like pthread_cond_init exist in two versions:
2.2 (older pthread_cond_t) and 2.3.2 (newer pthread_cond_t). In glibc
versions prior to 2.36, using an unversioned interceptor
(dlsym(RTLD_NEXT, "pthread_cond_init")) retrieves the older 2.2
symbol, which is not desired
(https://sourceware.org/bugzilla/show_bug.cgi?id=14932).

For newer architectures, such as aarch64 (introduced in glibc 2.17) and
riscv64 (introduced in glibc 2.27), a versioned interceptor is
unnecessary.


Full diff: https://github.com/llvm/llvm-project/pull/154268.diff

1 Files Affected:

  • (modified) compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp (+15-16)
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index e9f8d311bbce1..1e05c00ca85a6 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -79,17 +79,6 @@ struct ucontext_t {
 };
 #endif
 
-#if defined(__x86_64__) || defined(__mips__) || SANITIZER_PPC64V1 || \
-    defined(__s390x__)
-#define PTHREAD_ABI_BASE  "GLIBC_2.3.2"
-#elif defined(__aarch64__) || SANITIZER_PPC64V2
-#define PTHREAD_ABI_BASE  "GLIBC_2.17"
-#elif SANITIZER_LOONGARCH64
-#define PTHREAD_ABI_BASE  "GLIBC_2.36"
-#elif SANITIZER_RISCV64
-#  define PTHREAD_ABI_BASE "GLIBC_2.27"
-#endif
-
 extern "C" int pthread_attr_init(void *attr);
 extern "C" int pthread_attr_destroy(void *attr);
 DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *)
@@ -341,11 +330,6 @@ void ScopedInterceptor::DisableIgnoresImpl() {
 }
 
 #define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
-#if SANITIZER_FREEBSD || SANITIZER_NETBSD
-#  define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
-#else
-#  define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
-#endif
 #if SANITIZER_FREEBSD
 #  define TSAN_MAYBE_INTERCEPT_FREEBSD_ALIAS(func) \
     INTERCEPT_FUNCTION(_pthread_##func)
@@ -3041,12 +3025,27 @@ void InitializeInterceptors() {
   TSAN_INTERCEPT(pthread_timedjoin_np);
   #endif
 
+  // In glibc versions older than 2.36, dlsym(RTLD_NEXT, "pthread_cond_init")
+  // may return an outdated symbol (max(2.2,base_version)) if the port was
+  // introduced before 2.3.2 (when the new pthread_cond_t was introduced).
+#if SANITIZER_GLIBC && !__GLIBC_PREREQ(2, 36) &&                      \
+    (defined(__x86_64__) || defined(__mips__) || SANITIZER_PPC64V1 || \
+     defined(__s390x__))
+#  define PTHREAD_ABI_BASE "GLIBC_2.3.2"
   TSAN_INTERCEPT_VER(pthread_cond_init, PTHREAD_ABI_BASE);
   TSAN_INTERCEPT_VER(pthread_cond_signal, PTHREAD_ABI_BASE);
   TSAN_INTERCEPT_VER(pthread_cond_broadcast, PTHREAD_ABI_BASE);
   TSAN_INTERCEPT_VER(pthread_cond_wait, PTHREAD_ABI_BASE);
   TSAN_INTERCEPT_VER(pthread_cond_timedwait, PTHREAD_ABI_BASE);
   TSAN_INTERCEPT_VER(pthread_cond_destroy, PTHREAD_ABI_BASE);
+#else
+  TSAN_INTERCEPT(pthread_cond_init);
+  TSAN_INTERCEPT(pthread_cond_signal);
+  TSAN_INTERCEPT(pthread_cond_broadcast);
+  TSAN_INTERCEPT(pthread_cond_wait);
+  TSAN_INTERCEPT(pthread_cond_timedwait);
+  TSAN_INTERCEPT(pthread_cond_destroy);
+#endif
 
   TSAN_MAYBE_PTHREAD_COND_CLOCKWAIT;
 

.
Created using spr 1.3.5-bogner
Comment on lines -344 to -348
#if SANITIZER_FREEBSD || SANITIZER_NETBSD
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
#else
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can these be deleted? Where else would TSAN_INTERCEPT_VER be defined?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TSAN_INTERCEPT_VER is only used by these pthread_cond_* functions and is removed as it's no longer used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aren't they still used in lines 3034-3039 of this file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. Fixed:)

@MaskRay MaskRay requested a review from thurstond August 19, 2025 16:43
Copy link
Contributor

@cjappl cjappl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me - I'll follow this pattern in rtsan when it's merged

Created using spr 1.3.5-bogner
// may return an outdated symbol (max(2.2,base_version)) if the port was
// introduced before 2.3.2 (when the new pthread_cond_t was introduced).
#if SANITIZER_GLIBC && !__GLIBC_PREREQ(2, 36) && \
(defined(__x86_64__) || defined(__mips__) || SANITIZER_PPC64V1 || \
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why CPUs make a difference?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new pthread_cond symbols were introduced at 2.3.2. If a port (arch) was introduced after 2.3.2, the compatibility symbol (of non-default version) would not compiled, and there will only be one pthread_cond_init symbol, no need for dlvsym.

@MaskRay MaskRay merged commit c940c24 into main Aug 20, 2025
9 checks passed
@MaskRay MaskRay deleted the users/MaskRay/spr/tsan-refine-conditions-to-intercept-pthread_cond_t-functions branch August 20, 2025 03:07
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Aug 20, 2025
On glibc x86-64, functions like pthread_cond_init exist in two versions:
2.2 (older pthread_cond_t) and 2.3.2 (newer pthread_cond_t). In glibc
versions prior to 2.36, using an unversioned interceptor
(`dlsym(RTLD_NEXT, "pthread_cond_init")`) retrieves the older 2.2
symbol, which is not desired
(https://sourceware.org/bugzilla/show_bug.cgi?id=14932).

For newer architectures, such as aarch64 (introduced in glibc 2.17) and
riscv64 (introduced in glibc 2.27), a versioned interceptor is
unnecessary.

Pull Request: llvm/llvm-project#154268
@kongy
Copy link
Collaborator

kongy commented Aug 20, 2025

This change broke our musl build:

FAILED: compiler-rt/lib/tsan/dd/CMakeFiles/RTDD.x86_64.dir/dd_interceptors.cpp.o 
/b/f/w/src/git/out/stage2/./bin/clang++ --target=x86_64-unknown-linux-musl --sysroot=/b/f/w/src/git/prebuilts/build-tools/sysroots/x86_64-unknown-linux-musl -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/b/f/w/src/git/out/llvm-project/compiler-rt/lib/tsan/dd/../.. -march=x86-64-v2 --target=x86_64-unknown-linux-musl -D_LIBCPP_HAS_MUSL_LIBC -D_LARGEFILE64_SOURCE=1 -include stdc-predef.h  -Wno-unused-command-line-argument -D_LIBCPP_AVAILABILITY_HAS_NO_VERBOSE_ABORT=1 -mllvm -regalloc-enable-advisor=release  -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wall -Wno-unused-parameter -O3 -DNDEBUG -fPIC -fPIC -fno-builtin -fno-exceptions -fomit-frame-pointer -funwind-tables -fno-stack-protector -fno-sanitize=safe-stack -fvisibility=hidden -fno-lto -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta -O3 -gline-tables-only -Wno-gnu -Wno-variadic-macros -Wno-c99-extensions -ftrivial-auto-var-init=pattern -nostdinc++ -fno-rtti -std=c++17 -MD -MT compiler-rt/lib/tsan/dd/CMakeFiles/RTDD.x86_64.dir/dd_interceptors.cpp.o -MF compiler-rt/lib/tsan/dd/CMakeFiles/RTDD.x86_64.dir/dd_interceptors.cpp.o.d -o compiler-rt/lib/tsan/dd/CMakeFiles/RTDD.x86_64.dir/dd_interceptors.cpp.o -c /b/f/w/src/git/out/llvm-project/compiler-rt/lib/tsan/dd/dd_interceptors.cpp
b/f/w/src/git/out/llvm-project/compiler-rt/lib/tsan/dd/dd_interceptors.cpp:31639: error: token is not a valid binary operator in a preprocessor subexpression
  316 | #if SANITIZER_GLIBC && !__GLIBC_PREREQ(2, 36) &&                      \
      |                        ~~~~~~~~~~~~~~~^
1 error generated.

kongy added a commit that referenced this pull request Aug 21, 2025
The change in PR #154268 introduced a dependency on the `__GLIBC_PREREQ`
macro, which is not defined in musl libc. This caused the build to fail
in environments using musl.

This patch fixes the build by including
`sanitizer_common/sanitizer_glibc_version.h`. This header provides a
fallback definition for `__GLIBC_PREREQ` when LLVM is built against
non-glibc C libraries, resolving the compilation error.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants