summaryrefslogtreecommitdiff
path: root/compiler-rt
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-06-24 19:49:32 +0000
committerDmitry Vyukov <dvyukov@google.com>2015-06-24 19:49:32 +0000
commit7b07f91a4addf4e2ee80b20ca70ad6781f8b03fe (patch)
tree2c894a9a5c629453104a9ffd84564bcaad9d27ad /compiler-rt
parentcea26b549348f65d7390feb57cba66e196f2f572 (diff)
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.
Diffstat (limited to 'compiler-rt')
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_interceptors.cc41
-rw-r--r--compiler-rt/test/tsan/dl_iterate_phdr.cc54
2 files changed, 95 insertions, 0 deletions
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 <dlfcn.h>
+#include <link.h>
+#include <string.h>
+#include <string>
+
+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