Skip to content

Commit

Permalink
[tsan][llvm] Implement the function attribute to disable TSan checkin…
Browse files Browse the repository at this point in the history
…g at run time

This implements a function annotation that disables TSan checking for the
function at run time. The benefit over attribute((no_sanitize("thread")))
is that the accesses within the callees will also be suppressed.

The motivation for this attribute is a guarantee given by the objective C
language that the calls to the reference count decrement and object
deallocation will be synchronized. To model this properly, we would need
to intercept all ref count decrement calls (which are very common in ObjC
due to use of ARC) and also every single message send. Instead, we propose
to just ignore all accesses made from within dealloc at run time. The main
downside is that this still does not introduce any synchronization, which
means we might still report false positives if the code that relies on this
synchronization is not executed from within dealloc. However, we have not seen
this in practice so far and think these cases will be very rare.

Differential Revision: https://reviews.llvm.org/D25858

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@286663 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
AnnaZaks committed Nov 11, 2016
1 parent 7f0fc37 commit 1a26404
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 0 deletions.
23 changes: 23 additions & 0 deletions lib/Transforms/Instrumentation/ThreadSanitizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,15 @@ struct ThreadSanitizer : public FunctionPass {
const DataLayout &DL);
bool addrPointsToConstantData(Value *Addr);
int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
void InsertRuntimeIgnores(Function &F, SmallVector<Instruction*, 8> &RetVec);

Type *IntptrTy;
IntegerType *OrdTy;
// Callbacks to run-time library are computed in doInitialization.
Function *TsanFuncEntry;
Function *TsanFuncExit;
Function *TsanIgnoreBegin;
Function *TsanIgnoreEnd;
// Accesses sizes are powers of two: 1, 2, 4, 8, 16.
static const size_t kNumberOfAccessSizes = 5;
Function *TsanRead[kNumberOfAccessSizes];
Expand Down Expand Up @@ -152,6 +155,10 @@ void ThreadSanitizer::initializeCallbacks(Module &M) {
"__tsan_func_entry", IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
TsanFuncExit = checkSanitizerInterfaceFunction(
M.getOrInsertFunction("__tsan_func_exit", IRB.getVoidTy(), nullptr));
TsanIgnoreBegin = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
"__tsan_ignore_thread_begin", IRB.getVoidTy(), nullptr));
TsanIgnoreEnd = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
"__tsan_ignore_thread_end", IRB.getVoidTy(), nullptr));
OrdTy = IRB.getInt32Ty();
for (size_t i = 0; i < kNumberOfAccessSizes; ++i) {
const unsigned ByteSize = 1U << i;
Expand Down Expand Up @@ -376,6 +383,16 @@ static bool isAtomic(Instruction *I) {
return false;
}

void ThreadSanitizer::InsertRuntimeIgnores(Function &F,
SmallVector<Instruction*, 8> &RetVec) {
IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
IRB.CreateCall(TsanIgnoreBegin);
for (auto RetInst : RetVec) {
IRBuilder<> IRB(RetInst);
IRB.CreateCall(TsanIgnoreEnd);
}
}

bool ThreadSanitizer::runOnFunction(Function &F) {
// This is required to prevent instrumenting call to __tsan_init from within
// the module constructor.
Expand Down Expand Up @@ -438,6 +455,12 @@ bool ThreadSanitizer::runOnFunction(Function &F) {
Res |= instrumentMemIntrinsic(Inst);
}

if (F.hasFnAttribute("sanitize_thread_no_checking_at_run_time")) {
assert(!F.hasFnAttribute(Attribute::SanitizeThread));
if (HasCalls)
InsertRuntimeIgnores(F, RetVec);
}

// Instrument function entry/exit points if there were instrumented accesses.
if ((Res || HasCalls) && ClInstrumentFuncEntryExit) {
IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
; RUN: opt < %s -tsan -S | FileCheck %s

target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-unknown-linux-gnu"

define i32 @"\01-[NoCalls dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" {
entry:
%tmp1 = load i32, i32* %a, align 4
ret i32 %tmp1
}

; CHECK: define i32 @"\01-[NoCalls dealloc]"(i32* %a)
; CHECK-NEXT: entry:
; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4
; CHECK-NEXT: ret i32 %tmp1

declare void @"foo"()

define i32 @"\01-[WithCalls dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" {
entry:
%tmp1 = load i32, i32* %a, align 4
call void @foo()
ret i32 %tmp1
}

; CHECK: define i32 @"\01-[WithCalls dealloc]"(i32* %a)
; CHECK-NEXT: entry:
; CHECK-NEXT: %0 = call i8* @llvm.returnaddress(i32 0)
; CHECK-NEXT: call void @__tsan_func_entry(i8* %0)
; CHECK-NEXT: call void @__tsan_ignore_thread_begin()
; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4
; CHECK-NEXT: call void @foo()
; CHECK-NEXT: call void @__tsan_ignore_thread_end()
; CHECK-NEXT: call void @__tsan_func_exit()
; CHECK-NEXT: ret i32 %tmp1

0 comments on commit 1a26404

Please sign in to comment.