forked from OSchip/llvm-project
43 lines
1.4 KiB
C++
43 lines
1.4 KiB
C++
|
// RUN: %clang_scudo %s -o %t
|
||
|
// RUN: %run %t 2>&1
|
||
|
|
||
|
#include <locale.h>
|
||
|
#include <pthread.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
// 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;
|
||
|
}
|