From 7b07f91a4addf4e2ee80b20ca70ad6781f8b03fe Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Wed, 24 Jun 2015 19:49:32 +0000 Subject: tsan: fix false positive between dlopen and dl_iterate_phdr We see false reports between dlopen and dl_iterate_phdr. This happens because tsan does not see dynamic linker internal synchronization. Unpoison module names in dl_iterate_phdr callback. --- compiler-rt/lib/tsan/rtl/tsan_interceptors.cc | 41 ++++++++++++++++++++ compiler-rt/test/tsan/dl_iterate_phdr.cc | 54 +++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 compiler-rt/test/tsan/dl_iterate_phdr.cc (limited to 'compiler-rt') diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc index ca3be644af1..fbe66888d14 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors.cc @@ -2144,6 +2144,46 @@ TSAN_INTERCEPTOR(int, vfork, int fake) { return WRAP(fork)(fake); } +typedef int (*dl_iterate_phdr_cb_t)(__sanitizer_dl_phdr_info *info, SIZE_T size, + void *data); +struct dl_iterate_phdr_data { + ThreadState *thr; + uptr pc; + dl_iterate_phdr_cb_t cb; + void *data; +}; + +static int dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, + void *data) { + dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; + // dlopen/dlclose allocate/free dynamic-linker-internal memory, which is later + // accessible in dl_iterate_phdr callback. But we don't see synchronization + // inside of dynamic linker, so we "unpoison" it here in order to not + // produce false reports. Ignoring malloc/free in dlopen/dlclose is not enough + // because some libc functions call __libc_dlopen. + bool reset = info && IsAppMem((uptr)info->dlpi_name) && + *(u64*)MemToShadow((uptr)info->dlpi_name) != kShadowRodata; + if (reset) + MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name, + internal_strlen(info->dlpi_name)); + int res = cbdata->cb(info, size, cbdata->data); + if (reset) + MemoryResetRange(cbdata->thr, cbdata->pc, (uptr)info->dlpi_name, + internal_strlen(info->dlpi_name)); + return res; +} + +TSAN_INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb_t cb, void *data) { + SCOPED_TSAN_INTERCEPTOR(dl_iterate_phdr, cb, data); + dl_iterate_phdr_data cbdata; + cbdata.thr = thr; + cbdata.pc = pc; + cbdata.cb = cb; + cbdata.data = data; + int res = REAL(dl_iterate_phdr)(dl_iterate_phdr_cb, &cbdata); + return res; +} + static int OnExit(ThreadState *thr) { int status = Finalize(thr); FlushStreams(); @@ -2577,6 +2617,7 @@ void InitializeInterceptors() { TSAN_INTERCEPT(fork); TSAN_INTERCEPT(vfork); + TSAN_INTERCEPT(dl_iterate_phdr); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); TSAN_INTERCEPT(_exit); diff --git a/compiler-rt/test/tsan/dl_iterate_phdr.cc b/compiler-rt/test/tsan/dl_iterate_phdr.cc new file mode 100644 index 00000000000..6fb6a3e5497 --- /dev/null +++ b/compiler-rt/test/tsan/dl_iterate_phdr.cc @@ -0,0 +1,54 @@ +// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so +// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s + +// If we mention TSAN_OPTIONS, the test won't run from test_output.sh script. + +#ifdef BUILD_SO + +#include "test.h" + +int exported_var = 0; + +#else // BUILD_SO + +#include "test.h" +#include +#include +#include +#include + +static int callback(struct dl_phdr_info *info, size_t size, void *data) { + return !strcmp(info->dlpi_name, "non existent module"); +} + +void *thread(void *unused) { + for (int i = 0; i < 1000; i++) { + barrier_wait(&barrier); + dl_iterate_phdr(callback, 0); + } + return 0; +} + +int main(int argc, char *argv[]) { + barrier_init(&barrier, 2); + std::string path = std::string(argv[0]) + std::string("-so.so"); + pthread_t th; + pthread_create(&th, 0, thread, 0); + for (int i = 0; i < 1000; i++) { + barrier_wait(&barrier); + void *lib = dlopen(path.c_str(), RTLD_NOW); + if (!lib) { + printf("error in dlopen: %s\n", dlerror()); + return 1; + } + dlclose(lib); + } + pthread_join(th, 0); + printf("DONE\n"); + return 0; +} + +#endif // BUILD_SO + +// CHECK-NOT: WARNING: ThreadSanitizer: data race +// CHECK: DONE -- cgit v1.2.3