// RUN: %clang_scudo %s -o %t // RUN: %run %t 2>&1 #include #include #include #include #include // Some of glibc's own thread local data is destroyed after a user's thread // local destructors are called, via __libc_thread_freeres. This might involve // calling free, as is the case for strerror_thread_freeres. // If there is no prior heap operation in the thread, this free would end up // initializing some thread specific data that would never be destroyed // properly, while still being deallocated when the TLS goes away. As a result, // a program could SEGV, usually in // __sanitizer::AllocatorGlobalStats::Unregister, where one of the doubly // linked list links would refer to a now unmapped memory area. // This test reproduces those circumstances. Success means executing without // a segmentation fault. const int kNumThreads = 16; pthread_t tid[kNumThreads]; void *thread_func(void *arg) { uintptr_t i = (uintptr_t)arg; if ((i & 1) == 0) free(malloc(16)); // Calling strerror_l allows for strerror_thread_freeres to be called. strerror_l(0, LC_GLOBAL_LOCALE); return 0; } int main(int argc, char** argv) { for (uintptr_t j = 0; j < 8; j++) { for (uintptr_t i = 0; i < kNumThreads; i++) pthread_create(&tid[i], 0, thread_func, (void *)i); for (uintptr_t i = 0; i < kNumThreads; i++) pthread_join(tid[i], 0); } return 0; }