On Darwin, the setting ignore_noninstrumented_modules is used to suppress false positives in code that users don't have control of. The recently added "external" API (which can be used to detect races on objects provided by system libraries, but the race is actually user's fault) ignores this flag and it can report issues in non-instrumented modules. This patch fixes that.
Differential Revision: https://reviews.llvm.org/D31553
llvm-svn: 301000
//
//===----------------------------------------------------------------------===//
#include "tsan_rtl.h"
+#include "tsan_interceptors.h"
namespace __tsan {
CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
ThreadState *thr = cur_thread();
thr->external_tag = (uptr)tag;
- FuncEntry(thr, (uptr)caller_pc);
- MemoryRead(thr, CALLERPC, (uptr)addr, kSizeLog8);
- FuncExit(thr);
+ if (caller_pc) FuncEntry(thr, (uptr)caller_pc);
+ bool in_ignored_lib;
+ if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) {
+ MemoryRead(thr, CALLERPC, (uptr)addr, kSizeLog8);
+ }
+ if (caller_pc) FuncExit(thr);
thr->external_tag = 0;
}
CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed));
ThreadState *thr = cur_thread();
thr->external_tag = (uptr)tag;
- FuncEntry(thr, (uptr)caller_pc);
- MemoryWrite(thr, CALLERPC, (uptr)addr, kSizeLog8);
- FuncExit(thr);
+ if (caller_pc) FuncEntry(thr, (uptr)caller_pc);
+ bool in_ignored_lib;
+ if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) {
+ MemoryWrite(thr, CALLERPC, (uptr)addr, kSizeLog8);
+ }
+ if (caller_pc) FuncExit(thr);
thr->external_tag = 0;
}
} // extern "C"
// The object is 64-byte aligned, because we want hot data to be located in
// a single cache line if possible (it's accessed in every interceptor).
static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)];
-static LibIgnore *libignore() {
+LibIgnore *libignore() {
return reinterpret_cast<LibIgnore*>(&libignore_placeholder[0]);
}
bool ignoring_;
};
+LibIgnore *libignore();
+
} // namespace __tsan
#define SCOPED_INTERCEPTOR_RAW(func, ...) \
--- /dev/null
+// RUN: %clangxx_tsan -shared %p/external-lib.cc -fno-sanitize=thread -DUSE_TSAN_CALLBACKS \
+// RUN: -o %t-lib.dylib -install_name @rpath/`basename %t-lib.dylib`
+
+// RUN: %clangxx_tsan -shared %p/external-noninstrumented-module.cc %t-lib.dylib -fno-sanitize=thread \
+// RUN: -o %t-module.dylib -install_name @rpath/`basename %t-module.dylib`
+
+// RUN: %clangxx_tsan %s %t-module.dylib -o %t
+// RUN: %run %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+
+extern "C" void NonInstrumentedModule();
+int main(int argc, char *argv[]) {
+ NonInstrumentedModule();
+ fprintf(stderr, "Done.\n");
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: Done.
--- /dev/null
+// This file is used from other tests.
+// RUN: true
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct MyObject;
+typedef MyObject *MyObjectRef;
+extern "C" {
+ void InitializeLibrary();
+ MyObject *ObjectCreate();
+ long ObjectRead(MyObject *);
+ void ObjectWrite(MyObject *, long);
+ void ObjectWriteAnother(MyObject *, long);
+}
+
+struct MyObject {
+ long _val;
+ long _another;
+};
+
+#if defined(USE_TSAN_CALLBACKS)
+static void *tag;
+void *(*callback_register_tag)(const char *object_type);
+void *(*callback_assign_tag)(void *addr, void *tag);
+void (*callback_read)(void *addr, void *caller_pc, void *tag);
+void (*callback_write)(void *addr, void *caller_pc, void *tag);
+#endif
+
+void InitializeLibrary() {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_register_tag = (decltype(callback_register_tag))dlsym(RTLD_DEFAULT, "__tsan_external_register_tag");
+ callback_assign_tag = (decltype(callback_assign_tag))dlsym(RTLD_DEFAULT, "__tsan_external_assign_tag");
+ callback_read = (decltype(callback_read))dlsym(RTLD_DEFAULT, "__tsan_external_read");
+ callback_write = (decltype(callback_write))dlsym(RTLD_DEFAULT, "__tsan_external_write");
+ tag = callback_register_tag("MyLibrary::MyObject");
+#endif
+}
+
+MyObject *ObjectCreate() {
+ MyObject *ref = (MyObject *)malloc(sizeof(MyObject));
+#if defined(USE_TSAN_CALLBACKS)
+ callback_assign_tag(ref, tag);
+#endif
+ return ref;
+}
+
+long ObjectRead(MyObject *ref) {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_read(ref, __builtin_return_address(0), tag);
+#endif
+ return ref->_val;
+}
+
+void ObjectWrite(MyObject *ref, long val) {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_write(ref, __builtin_return_address(0), tag);
+#endif
+ ref->_val = val;
+}
+
+void ObjectWriteAnother(MyObject *ref, long val) {
+#if defined(USE_TSAN_CALLBACKS)
+ callback_write(ref, __builtin_return_address(0), tag);
+#endif
+ ref->_another = val;
+}
--- /dev/null
+// This file is used from other tests.
+// RUN: true
+
+#include <thread>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+struct MyObject;
+typedef MyObject *MyObjectRef;
+extern "C" {
+ void InitializeLibrary();
+ MyObject *ObjectCreate();
+ long ObjectRead(MyObject *);
+ void ObjectWrite(MyObject *, long);
+ void ObjectWriteAnother(MyObject *, long);
+}
+
+extern "C" void NonInstrumentedModule() {
+ InitializeLibrary();
+
+ MyObjectRef ref = ObjectCreate();
+ std::thread t1([ref]{ ObjectWrite(ref, 42); });
+ std::thread t2([ref]{ ObjectWrite(ref, 43); });
+ t1.join();
+ t2.join();
+}
-// RUN: %clangxx_tsan %s -shared -DSHARED_LIB \
+// RUN: %clangxx_tsan %p/external-lib.cc -shared \
// RUN: -o %t-lib-instrumented.dylib \
// RUN: -install_name @rpath/`basename %t-lib-instrumented.dylib`
-// RUN: %clangxx_tsan %s -shared -DSHARED_LIB -fno-sanitize=thread \
+// RUN: %clangxx_tsan %p/external-lib.cc -shared -fno-sanitize=thread \
// RUN: -o %t-lib-noninstrumented.dylib \
// RUN: -install_name @rpath/`basename %t-lib-noninstrumented.dylib`
-// RUN: %clangxx_tsan %s -shared -DSHARED_LIB -fno-sanitize=thread -DUSE_TSAN_CALLBACKS \
+// RUN: %clangxx_tsan %p/external-lib.cc -shared -fno-sanitize=thread -DUSE_TSAN_CALLBACKS \
// RUN: -o %t-lib-noninstrumented-callbacks.dylib \
// RUN: -install_name @rpath/`basename %t-lib-noninstrumented-callbacks.dylib`
#include <thread>
-#include <dlfcn.h>
-#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
void ObjectWriteAnother(MyObject *, long);
}
-#if defined(SHARED_LIB)
-
-struct MyObject {
- long _val;
- long _another;
-};
-
-#if defined(USE_TSAN_CALLBACKS)
-static void *tag;
-void *(*callback_register_tag)(const char *object_type);
-void *(*callback_assign_tag)(void *addr, void *tag);
-void (*callback_read)(void *addr, void *caller_pc, void *tag);
-void (*callback_write)(void *addr, void *caller_pc, void *tag);
-#endif
-
-void InitializeLibrary() {
-#if defined(USE_TSAN_CALLBACKS)
- callback_register_tag = (decltype(callback_register_tag))dlsym(RTLD_DEFAULT, "__tsan_external_register_tag");
- callback_assign_tag = (decltype(callback_assign_tag))dlsym(RTLD_DEFAULT, "__tsan_external_assign_tag");
- callback_read = (decltype(callback_read))dlsym(RTLD_DEFAULT, "__tsan_external_read");
- callback_write = (decltype(callback_write))dlsym(RTLD_DEFAULT, "__tsan_external_write");
- tag = callback_register_tag("MyLibrary::MyObject");
-#endif
-}
-
-MyObject *ObjectCreate() {
- MyObject *ref = (MyObject *)malloc(sizeof(MyObject));
-#if defined(USE_TSAN_CALLBACKS)
- callback_assign_tag(ref, tag);
-#endif
- return ref;
-}
-
-long ObjectRead(MyObject *ref) {
-#if defined(USE_TSAN_CALLBACKS)
- callback_read(ref, __builtin_return_address(0), tag);
-#endif
- return ref->_val;
-}
-
-void ObjectWrite(MyObject *ref, long val) {
-#if defined(USE_TSAN_CALLBACKS)
- callback_write(ref, __builtin_return_address(0), tag);
-#endif
- ref->_val = val;
-}
-
-void ObjectWriteAnother(MyObject *ref, long val) {
-#if defined(USE_TSAN_CALLBACKS)
- callback_write(ref, __builtin_return_address(0), tag);
-#endif
- ref->_another = val;
-}
-
-#else // defined(SHARED_LIB)
-
int main(int argc, char *argv[]) {
InitializeLibrary();
fprintf(stderr, "WW test done\n");
// CHECK: WW test done
}
-
-#endif // defined(SHARED_LIB)