llvm.org GIT mirror llvm / a5ffeed
MSan: handle llvm.lifetime.start intrinsic Summary: When a variable goes into scope several times within a single function or when two variables from different scopes share a stack slot it may be incorrect to poison such scoped locals at the beginning of the function. In the former case it may lead to false negatives (see https://github.com/google/sanitizers/issues/590), in the latter - to incorrect reports (because only one origin remains on the stack). If Clang emits lifetime intrinsics for such scoped variables we insert code poisoning them after each call to llvm.lifetime.start(). If for a certain intrinsic we fail to find a corresponding alloca, we fall back to poisoning allocas for the whole function, as it's now impossible to tell which alloca was missed. The new instrumentation may slow down hot loops containing local variables with lifetime intrinsics, so we allow disabling it with -mllvm -msan-handle-lifetime-intrinsics=false. Reviewers: eugenis, pcc Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D60617 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@359536 91177308-0d34-0410-b5e6-96231b3b80d8 Alexander Potapenko 5 months ago
2 changed file(s) with 183 addition(s) and 8 deletion(s). Raw diff Collapse all Expand all
142142 #include "llvm/ADT/APInt.h"
143143 #include "llvm/ADT/ArrayRef.h"
144144 #include "llvm/ADT/DepthFirstIterator.h"
145 #include "llvm/ADT/SmallSet.h"
145146 #include "llvm/ADT/SmallString.h"
146147 #include "llvm/ADT/SmallVector.h"
147148 #include "llvm/ADT/StringExtras.h"
245246 static cl::opt ClHandleICmpExact("msan-handle-icmp-exact",
246247 cl::desc("exact handling of relational integer ICmp"),
247248 cl::Hidden, cl::init(false));
249
250 static cl::opt ClHandleLifetimeIntrinsics(
251 "msan-handle-lifetime-intrinsics",
252 cl::desc(
253 "when possible, poison scoped variables at the beginning of the scope "
254 "(slower, but more precise)"),
255 cl::Hidden, cl::init(true));
248256
249257 // When compiling the Linux kernel, we sometimes see false positives related to
250258 // MSan being unable to understand that inline assembly calls may initialize
10221030 : Shadow(S), Origin(O), OrigIns(I) {}
10231031 };
10241032 SmallVector InstrumentationList;
1033 bool InstrumentLifetimeStart = ClHandleLifetimeIntrinsics;
1034 SmallSet AllocaSet;
1035 SmallVector, 16> LifetimeStartList;
10251036 SmallVector StoreList;
10261037
10271038 MemorySanitizerVisitor(Function &F, MemorySanitizer &MS,
12771288 }
12781289
12791290 VAHelper->finalizeInstrumentation();
1291
1292 // Poison llvm.lifetime.start intrinsics, if we haven't fallen back to
1293 // instrumenting only allocas.
1294 if (InstrumentLifetimeStart) {
1295 for (auto Item : LifetimeStartList) {
1296 instrumentAlloca(*Item.second, Item.first);
1297 AllocaSet.erase(Item.second);
1298 }
1299 }
1300 // Poison the allocas for which we didn't instrument the corresponding
1301 // lifetime intrinsics.
1302 for (AllocaInst *AI : AllocaSet)
1303 instrumentAlloca(*AI);
12801304
12811305 bool InstrumentWithCalls = ClInstrumentationWithCallThreshold >= 0 &&
12821306 InstrumentationList.size() + StoreList.size() >
25352559 return false;
25362560 }
25372561
2562 void handleLifetimeStart(IntrinsicInst &I) {
2563 if (!PoisonStack)
2564 return;
2565 DenseMap AllocaForValue;
2566 AllocaInst *AI =
2567 llvm::findAllocaForValue(I.getArgOperand(1), AllocaForValue);
2568 if (!AI)
2569 InstrumentLifetimeStart = false;
2570 LifetimeStartList.push_back(std::make_pair(&I, AI));
2571 }
2572
25382573 void handleBswap(IntrinsicInst &I) {
25392574 IRBuilder<> IRB(&I);
25402575 Value *Op = I.getArgOperand(0);
29502985
29512986 void visitIntrinsicInst(IntrinsicInst &I) {
29522987 switch (I.getIntrinsicID()) {
2988 case Intrinsic::lifetime_start:
2989 handleLifetimeStart(I);
2990 break;
29532991 case Intrinsic::bswap:
29542992 handleBswap(I);
29552993 break;
33783416 StackDescription.str());
33793417 }
33803418
3381 void instrumentAllocaUserspace(AllocaInst &I, IRBuilder<> &IRB, Value *Len) {
3419 void poisonAllocaUserspace(AllocaInst &I, IRBuilder<> &IRB, Value *Len) {
33823420 if (PoisonStack && ClPoisonStackWithCall) {
33833421 IRB.CreateCall(MS.MsanPoisonStackFn,
33843422 {IRB.CreatePointerCast(&I, IRB.getInt8PtrTy()), Len});
34003438 }
34013439 }
34023440
3403 void instrumentAllocaKmsan(AllocaInst &I, IRBuilder<> &IRB, Value *Len) {
3441 void poisonAllocaKmsan(AllocaInst &I, IRBuilder<> &IRB, Value *Len) {
34043442 Value *Descr = getLocalVarDescription(I);
34053443 if (PoisonStack) {
34063444 IRB.CreateCall(MS.MsanPoisonAllocaFn,
34123450 }
34133451 }
34143452
3415 void visitAllocaInst(AllocaInst &I) {
3416 setShadow(&I, getCleanShadow(&I));
3417 setOrigin(&I, getCleanOrigin());
3418 IRBuilder<> IRB(I.getNextNode());
3453 void instrumentAlloca(AllocaInst &I, Instruction *InsPoint = nullptr) {
3454 if (!InsPoint)
3455 InsPoint = &I;
3456 IRBuilder<> IRB(InsPoint->getNextNode());
34193457 const DataLayout &DL = F.getParent()->getDataLayout();
34203458 uint64_t TypeSize = DL.getTypeAllocSize(I.getAllocatedType());
34213459 Value *Len = ConstantInt::get(MS.IntptrTy, TypeSize);
34233461 Len = IRB.CreateMul(Len, I.getArraySize());
34243462
34253463 if (MS.CompileKernel)
3426 instrumentAllocaKmsan(I, IRB, Len);
3464 poisonAllocaKmsan(I, IRB, Len);
34273465 else
3428 instrumentAllocaUserspace(I, IRB, Len);
3466 poisonAllocaUserspace(I, IRB, Len);
3467 }
3468
3469 void visitAllocaInst(AllocaInst &I) {
3470 setShadow(&I, getCleanShadow(&I));
3471 setOrigin(&I, getCleanOrigin());
3472 // We'll get to this alloca later unless it's poisoned at the corresponding
3473 // llvm.lifetime.start.
3474 AllocaSet.insert(&I);
34293475 }
34303476
34313477 void visitSelectInst(SelectInst& I) {
8888 ; KMSAN: call void @__msan_unpoison_alloca(i8* {{.*}}, i64 20)
8989 ; CHECK: ret void
9090
91 ; Check that every llvm.lifetime.start() causes poisoning of locals.
92 define void @lifetime_start() sanitize_memory {
93 entry:
94 %x = alloca i32, align 4
95 %c = bitcast i32* %x to i8*
96 br label %another_bb
97
98 another_bb:
99 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %c)
100 store i32 7, i32* %x
101 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %c)
102 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %c)
103 store i32 8, i32* %x
104 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %c)
105 ret void
106 }
107
108 ; CHECK-LABEL: define void @lifetime_start(
109 ; CHECK-LABEL: entry:
110 ; CHECK: %x = alloca i32
111 ; CHECK-LABEL: another_bb:
112
113 ; CHECK: call void @llvm.lifetime.start
114 ; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
115 ; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
116 ; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
117 ; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
118
119 ; CHECK: call void @llvm.lifetime.start
120 ; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
121 ; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
122 ; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
123 ; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
124 ; CHECK: ret void
125
126 ; Make sure variable-length arrays are handled correctly.
127 define void @lifetime_start_var(i64 %cnt) sanitize_memory {
128 entry:
129 %x = alloca i32, i64 %cnt, align 4
130 %c = bitcast i32* %x to i8*
131 call void @llvm.lifetime.start.p0i8(i64 -1, i8* nonnull %c)
132 call void @llvm.lifetime.end.p0i8(i64 -1, i8* nonnull %c)
133 ret void
134 }
135
136 ; CHECK-LABEL: define void @lifetime_start_var(
137 ; CHECK-LABEL: entry:
138 ; CHECK: %x = alloca i32, i64 %cnt
139 ; CHECK: call void @llvm.lifetime.start
140 ; CHECK: %[[A:.*]] = mul i64 4, %cnt
141 ; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 %[[A]], i1 false)
142 ; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 %[[A]])
143 ; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 %[[A]],
144 ; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 %[[A]],
145 ; CHECK: call void @llvm.lifetime.end
146 ; CHECK: ret void
147
148
149 ; If we can't trace one of the lifetime markers to a single alloca, fall back
150 ; to poisoning allocas at the beginning of the function.
151 ; Each alloca must be poisoned only once.
152 define void @lifetime_no_alloca(i8 %v) sanitize_memory {
153 entry:
154 %x = alloca i32, align 4
155 %y = alloca i32, align 4
156 %z = alloca i32, align 4
157 %cx = bitcast i32* %x to i8*
158 %cy = bitcast i32* %y to i8*
159 %cz = bitcast i32* %z to i8*
160 %tobool = icmp eq i8 %v, 0
161 %xy = select i1 %tobool, i32* %x, i32* %y
162 %cxcy = select i1 %tobool, i8* %cx, i8* %cy
163 br label %another_bb
164
165 another_bb:
166 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz)
167 store i32 7, i32* %z
168 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz)
169 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cz)
170 store i32 7, i32* %z
171 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cz)
172 call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %cxcy)
173 store i32 8, i32* %xy
174 call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %cxcy)
175 ret void
176 }
177
178 ; CHECK-LABEL: define void @lifetime_no_alloca(
179 ; CHECK-LABEL: entry:
180 ; CHECK: %x = alloca i32
181 ; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
182 ; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
183 ; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
184 ; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
185 ; CHECK: %y = alloca i32
186 ; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
187 ; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
188 ; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
189 ; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
190 ; CHECK: %z = alloca i32
191 ; INLINE: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
192 ; CALL: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
193 ; ORIGIN: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
194 ; KMSAN: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
195
196 ; There're two lifetime intrinsics for %z, but we must instrument it only once.
197 ; INLINE-NOT: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
198 ; CALL-NOT: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
199 ; ORIGIN-NOT: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
200 ; KMSAN-NOT: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
201 ; CHECK-LABEL: another_bb:
202
203 ; CHECK: call void @llvm.lifetime.start
204 ; INLINE-NOT: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
205 ; CALL-NOT: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
206 ; ORIGIN-NOT: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
207 ; KMSAN-NOT: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
208 ; CHECK: call void @llvm.lifetime.end
209 ; CHECK: call void @llvm.lifetime.start
210 ; INLINE-NOT: call void @llvm.memset.p0i8.i64(i8* align 4 {{.*}}, i8 -1, i64 4, i1 false)
211 ; CALL-NOT: call void @__msan_poison_stack(i8* {{.*}}, i64 4)
212 ; ORIGIN-NOT: call void @__msan_set_alloca_origin4(i8* {{.*}}, i64 4,
213 ; KMSAN-NOT: call void @__msan_poison_alloca(i8* {{.*}}, i64 4,
214 ; CHECK: call void @llvm.lifetime.end
215
216
217
218 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture)
219 declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture)