diff --git a/clang/docs/ThreadSanitizer.rst b/clang/docs/ThreadSanitizer.rst --- a/clang/docs/ThreadSanitizer.rst +++ b/clang/docs/ThreadSanitizer.rst @@ -100,6 +100,16 @@ traces. This attribute may not be supported by other compilers, so we suggest to use it together with ``__has_feature(thread_sanitizer)``. +``__attribute__((disable_sanitizer_instrumentation))`` +-------------------------------------------------------- + +The ``disable_sanitizer_instrumentation`` attribute can be applied to functions +to prevent all kinds of instrumentation. As a result, it may introduce false +positives and incorrect stack traces. Therefore, it should be used with care, +and only if absolutely required; for example for certain code that cannot +tolerate any instrumentation and resulting side-effects. This attribute +overrides ``no_sanitize("thread")``. + Ignorelist ---------- diff --git a/clang/test/CodeGen/sanitize-thread-disable.c b/clang/test/CodeGen/sanitize-thread-disable.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/sanitize-thread-disable.c @@ -0,0 +1,57 @@ +// RUN: %clang -target x86_64-linux-gnu -S -emit-llvm -o - %s | FileCheck -check-prefixes CHECK,WITHOUT %s +// RUN: %clang -target x86_64-linux-gnu -S -emit-llvm -o - %s -fsanitize=thread | FileCheck -check-prefixes CHECK,TSAN %s + +#include + +// Instrumented function. +// TSan inserts calls to __tsan_func_entry() and __tsan_func_exit() to prologue/epilogue. +// Non-atomic loads are instrumented with __tsan_readXXX(), atomic loads - with +// __tsan_atomicXXX_load(). +// +// CHECK-LABEL: @instrumented1 +// TSAN: call void @__tsan_func_entry +// WITHOUT-NOT: call void @__tsan_func_entry +// TSAN: call void @__tsan_read4 +// WITHOUT-NOT: call void @__tsan_read4 +// TSAN: call i32 @__tsan_atomic32_load +// WITHOUT-NOT: call i32 @__tsan_atomic32_load +// TSAN: call void @__tsan_func_exit +// WITHOUT-NOT: call void @__tsan_func_exit +// CHECK: ret i32 +int instrumented1(int *a, _Atomic int *b) { + return *a + atomic_load(b); +} + +// Function with no_sanitize("thread"). +// TSan only inserts instrumentation necessary to prevent false positives: calls are inserted for +// function entry/exit and atomics, but not plain memory accesses. +// +// CHECK-LABEL: @no_false_positives1 +// TSAN: call void @__tsan_func_entry +// WITHOUT-NOT: call void @__tsan_func_entry +// TSAN-NOT: call void @__tsan_read4 +// WITHOUT-NOT: call void @__tsan_read4 +// TSAN: call i32 @__tsan_atomic32_load +// WITHOUT-NOT: call i32 @__tsan_atomic32_load +// TSAN: call void @__tsan_func_exit +// WITHOUT-NOT: call void @__tsan_func_exit +// CHECK: ret i32 +__attribute__((no_sanitize("thread"))) int no_false_positives1(int *a, _Atomic int *b) { + return *a + atomic_load(b); +} + +// Function with disable_sanitizer_instrumentation: no instrumentation at all. +// +// CHECK-LABEL: @no_instrumentation1 +// TSAN-NOT: call void @__tsan_func_entry +// WITHOUT-NOT: call void @__tsan_func_entry +// TSAN-NOT: call void @__tsan_read4 +// WITHOUT-NOT: call void @__tsan_read4 +// TSAN-NOT: call i32 @__tsan_atomic32_load +// WITHOUT-NOT: call i32 @__tsan_atomic32_load +// TSAN-NOT: call void @__tsan_func_exit +// WITHOUT-NOT: call void @__tsan_func_exit +// CHECK: ret i32 +__attribute__((disable_sanitizer_instrumentation)) int no_instrumentation1(int *a, _Atomic int *b) { + return *a + atomic_load(b); +} diff --git a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/ThreadSanitizer.cpp @@ -562,6 +562,12 @@ // all. if (F.hasFnAttribute(Attribute::Naked)) return false; + + // __attribute__(disable_sanitizer_instrumentation) prevents all kinds of + // instrumentation. + if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) + return false; + initialize(*F.getParent()); SmallVector AllLoadsAndStores; SmallVector LocalLoadsAndStores;