llvm.org GIT mirror llvm / 1a26404
[tsan][llvm] Implement the function attribute to disable TSan checking 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 Anna Zaks 4 years ago
2 changed file(s) with 58 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
9898 const DataLayout &DL);
9999 bool addrPointsToConstantData(Value *Addr);
100100 int getMemoryAccessFuncIndex(Value *Addr, const DataLayout &DL);
101 void InsertRuntimeIgnores(Function &F, SmallVector &RetVec);
101102
102103 Type *IntptrTy;
103104 IntegerType *OrdTy;
104105 // Callbacks to run-time library are computed in doInitialization.
105106 Function *TsanFuncEntry;
106107 Function *TsanFuncExit;
108 Function *TsanIgnoreBegin;
109 Function *TsanIgnoreEnd;
107110 // Accesses sizes are powers of two: 1, 2, 4, 8, 16.
108111 static const size_t kNumberOfAccessSizes = 5;
109112 Function *TsanRead[kNumberOfAccessSizes];
151154 "__tsan_func_entry", IRB.getVoidTy(), IRB.getInt8PtrTy(), nullptr));
152155 TsanFuncExit = checkSanitizerInterfaceFunction(
153156 M.getOrInsertFunction("__tsan_func_exit", IRB.getVoidTy(), nullptr));
157 TsanIgnoreBegin = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
158 "__tsan_ignore_thread_begin", IRB.getVoidTy(), nullptr));
159 TsanIgnoreEnd = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
160 "__tsan_ignore_thread_end", IRB.getVoidTy(), nullptr));
154161 OrdTy = IRB.getInt32Ty();
155162 for (size_t i = 0; i < kNumberOfAccessSizes; ++i) {
156163 const unsigned ByteSize = 1U << i;
375382 return false;
376383 }
377384
385 void ThreadSanitizer::InsertRuntimeIgnores(Function &F,
386 SmallVector &RetVec) {
387 IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
388 IRB.CreateCall(TsanIgnoreBegin);
389 for (auto RetInst : RetVec) {
390 IRBuilder<> IRB(RetInst);
391 IRB.CreateCall(TsanIgnoreEnd);
392 }
393 }
394
378395 bool ThreadSanitizer::runOnFunction(Function &F) {
379396 // This is required to prevent instrumenting call to __tsan_init from within
380397 // the module constructor.
436453 for (auto Inst : MemIntrinCalls) {
437454 Res |= instrumentMemIntrinsic(Inst);
438455 }
456
457 if (F.hasFnAttribute("sanitize_thread_no_checking_at_run_time")) {
458 assert(!F.hasFnAttribute(Attribute::SanitizeThread));
459 if (HasCalls)
460 InsertRuntimeIgnores(F, RetVec);
461 }
439462
440463 // Instrument function entry/exit points if there were instrumented accesses.
441464 if ((Res || HasCalls) && ClInstrumentFuncEntryExit) {
0 ; RUN: opt < %s -tsan -S | FileCheck %s
1
2 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"
3 target triple = "x86_64-unknown-linux-gnu"
4
5 define i32 @"\01-[NoCalls dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" {
6 entry:
7 %tmp1 = load i32, i32* %a, align 4
8 ret i32 %tmp1
9 }
10
11 ; CHECK: define i32 @"\01-[NoCalls dealloc]"(i32* %a)
12 ; CHECK-NEXT: entry:
13 ; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4
14 ; CHECK-NEXT: ret i32 %tmp1
15
16 declare void @"foo"()
17
18 define i32 @"\01-[WithCalls dealloc]"(i32* %a) "sanitize_thread_no_checking_at_run_time" {
19 entry:
20 %tmp1 = load i32, i32* %a, align 4
21 call void @foo()
22 ret i32 %tmp1
23 }
24
25 ; CHECK: define i32 @"\01-[WithCalls dealloc]"(i32* %a)
26 ; CHECK-NEXT: entry:
27 ; CHECK-NEXT: %0 = call i8* @llvm.returnaddress(i32 0)
28 ; CHECK-NEXT: call void @__tsan_func_entry(i8* %0)
29 ; CHECK-NEXT: call void @__tsan_ignore_thread_begin()
30 ; CHECK-NEXT: %tmp1 = load i32, i32* %a, align 4
31 ; CHECK-NEXT: call void @foo()
32 ; CHECK-NEXT: call void @__tsan_ignore_thread_end()
33 ; CHECK-NEXT: call void @__tsan_func_exit()
34 ; CHECK-NEXT: ret i32 %tmp1