[compiler-rt] [rtsan] Support legacy pthread_cond variables (PR #152947)

Chris Apple via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 18 21:05:42 PDT 2025


================
@@ -46,12 +46,52 @@
 
 using namespace __sanitizer;
 
+#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
+
+DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, usize size)
+DECLARE_REAL_AND_INTERCEPTOR(void, free, void *ptr)
+
 namespace {
 struct DlsymAlloc : public DlSymAllocator<DlsymAlloc> {
   static bool UseImpl() { return !__rtsan_is_initialized(); }
 };
 } // namespace
 
+// See note in tsan as to why this is necessary
+static pthread_cond_t *init_cond(pthread_cond_t *c, bool force = false) {
+  if (!common_flags()->legacy_pthread_cond)
----------------
cjappl wrote:

If I delete this codepath, I get a crash on almost all tests on my mac, with this stack trace:

```
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3cb0b1bb)
  * frame #0: 0x000000018d619e4c libsystem_pthread.dylib`pthread_cond_broadcast + 48
    frame #1: 0x000000018d54ae70 libc++.1.dylib`std::__1::__call_once(unsigned long volatile&, void*, void (*)(void*)) + 224
    frame #2: 0x000000018d582914 libc++.1.dylib`std::__1::locale::id::__get() + 104
    frame #3: 0x000000018d57fb94 libc++.1.dylib`std::__1::locale::__imp::__imp(unsigned long) + 148
    frame #4: 0x000000018d580c10 libc++.1.dylib`std::__1::locale::classic() + 84
    frame #5: 0x000000018d582140 libc++.1.dylib`std::__1::locale::__global() + 72
    frame #6: 0x000000018d5821a0 libc++.1.dylib`std::__1::locale::locale() + 24
    frame #7: 0x000000018d55875c libc++.1.dylib`std::__1::basic_streambuf<char, std::__1::char_traits<char>>::basic_streambuf() + 52
    frame #8: 0x000000018d56aac8 libc++.1.dylib`std::__1::__stdinbuf<char>::__stdinbuf(__sFILE*, __mbstate_t*) + 40
    frame #9: 0x000000018d56a45c libc++.1.dylib`std::__1::DoIOSInit::DoIOSInit() + 68
    frame #10: 0x000000018d56aa50 libc++.1.dylib`std::__1::ios_base::Init::Init() + 80
    frame #11: 0x000000018d56be70 libc++.1.dylib`_GLOBAL__I_000100 + 32
    frame #12: 0x000000018d2b105c dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168
    frame #13: 0x000000018d2ef308 dyld`invocation function for block in dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 340
    frame #14: 0x000000018d2e299c dyld`invocation function for block in dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 496
    frame #15: 0x000000018d2922fc dyld`dyld3::MachOFile::forEachLoadCommand(Diagnostics&, void (load_command const*, bool&) block_pointer) const + 300
    frame #16: 0x000000018d2e1930 dyld`dyld3::MachOFile::forEachSection(void (dyld3::MachOFile::SectionInfo const&, bool, bool&) block_pointer) const + 192
    frame #17: 0x000000018d2eee1c dyld`dyld3::MachOAnalyzer::forEachInitializer(Diagnostics&, dyld3::MachOAnalyzer::VMAddrConverter const&, void (unsigned int) block_pointer, void const*) const + 516
    frame #18: 0x000000018d2ad070 dyld`dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const + 524
    frame #19: 0x000000018d2b6d28 dyld`dyld4::PrebuiltLoader::runInitializers(dyld4::RuntimeState&) const + 44
    frame #20: 0x000000018d2ad45c dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 220
    frame #21: 0x000000018d2ad400 dyld`dyld4::Loader::runInitializersBottomUp(dyld4::RuntimeState&, dyld3::Array<dyld4::Loader const*>&) const + 128
    frame #22: 0x000000018d2b10ec dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const::$_1::operator()() const + 116
    frame #23: 0x000000018d2ad628 dyld`dyld4::Loader::runInitializersBottomUpPlusUpwardLinks(dyld4::RuntimeState&) const + 380
    frame #24: 0x000000018d2d04d8 dyld`dyld4::APIs::runAllInitializersForMain() + 464
    frame #25: 0x000000018d296f7c dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3156
    frame #26: 0x000000018d295edc dyld`start + 1844
```

Specifically, the pthread_cond_broadcast interceptor gets called, but seemingly our `pthread_cond_init` function is never called. This means:

```cpp
INTERCEPTOR(int, pthread_cond_broadcast, pthread_cond_t *cond) {
  __rtsan_notify_intercepted_call("pthread_cond_broadcast");


  // This function returns a cond not initialized by us, 
  pthread_cond_t *c = init_cond(cond);

  // this call crashes, as it operates on a pthread_cond that wasn't initialized properly
  return REAL(pthread_cond_broadcast)(c);
}
```

Any thoughts on getting around this? I've tried a few things, but nothing seems to work.

https://github.com/llvm/llvm-project/pull/152947


More information about the llvm-commits mailing list