[tsan] Ignore memory accesses for libignored modules for "external" races

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
This commit is contained in:
Kuba Mracek 2017-04-21 17:18:14 +00:00
parent 9eb170fede
commit 2e4e7d04d2
7 changed files with 133 additions and 70 deletions

View File

@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
#include "tsan_rtl.h"
#include "tsan_interceptors.h"
namespace __tsan {
@ -57,9 +58,12 @@ void __tsan_external_read(void *addr, void *caller_pc, void *tag) {
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;
}
@ -68,9 +72,12 @@ void __tsan_external_write(void *addr, void *caller_pc, void *tag) {
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"

View File

@ -210,7 +210,7 @@ struct ThreadSignalContext {
// 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]);
}

View File

@ -19,6 +19,8 @@ class ScopedInterceptor {
bool ignoring_;
};
LibIgnore *libignore();
} // namespace __tsan
#define SCOPED_INTERCEPTOR_RAW(func, ...) \

View File

@ -0,0 +1,19 @@
// 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.

View File

@ -0,0 +1,68 @@
// 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;
}

View File

@ -0,0 +1,27 @@
// 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();
}

View File

@ -1,12 +1,12 @@
// 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`
@ -23,8 +23,6 @@
#include <thread>
#include <dlfcn.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
@ -38,62 +36,6 @@ extern "C" {
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();
@ -159,5 +101,3 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "WW test done\n");
// CHECK: WW test done
}
#endif // defined(SHARED_LIB)