1// RUN: %clang_scudo %s -o %t
2// RUN: %run %t 2>&1
3
4#include <locale.h>
5#include <pthread.h>
6#include <stdint.h>
7#include <stdlib.h>
8#include <string.h>
9
10// Some of glibc's own thread local data is destroyed after a user's thread
11// local destructors are called, via __libc_thread_freeres. This might involve
12// calling free, as is the case for strerror_thread_freeres.
13// If there is no prior heap operation in the thread, this free would end up
14// initializing some thread specific data that would never be destroyed
15// properly, while still being deallocated when the TLS goes away. As a result,
16// a program could SEGV, usually in
17// __sanitizer::AllocatorGlobalStats::Unregister, where one of the doubly
18// linked list links would refer to a now unmapped memory area.
19
20// This test reproduces those circumstances. Success means executing without
21// a segmentation fault.
22
23const int kNumThreads = 16;
24pthread_t tid[kNumThreads];
25
26void *thread_func(void *arg) {
27 uintptr_t i = (uintptr_t)arg;
28 if ((i & 1) == 0)
29 free(ptr: malloc(size: 16));
30 // Calling strerror_l allows for strerror_thread_freeres to be called.
31 strerror_l(errnum: 0, LC_GLOBAL_LOCALE);
32 return 0;
33}
34
35int main(int argc, char **argv) {
36 for (uintptr_t j = 0; j < 8; j++) {
37 for (uintptr_t i = 0; i < kNumThreads; i++)
38 pthread_create(newthread: &tid[i], attr: 0, start_routine: thread_func, arg: (void *)i);
39 for (uintptr_t i = 0; i < kNumThreads; i++)
40 pthread_join(th: tid[i], thread_return: 0);
41 }
42 return 0;
43}
44

source code of compiler-rt/test/scudo/tsd_destruction.c