Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -18,6 +18,7 @@ #include "llvm/Transforms/Instrumentation.h" #include "BlackList.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallSet.h" @@ -316,6 +317,20 @@ Function *AsanStackMallocFunc, *AsanStackFreeFunc; Function *AsanPoisonStackMemoryFunc, *AsanUnpoisonStackMemoryFunc; + // Stores a place and arguments of poisoning/unpoisoning call for alloca. + struct AllocaPoisonCall { + Instruction *InsBefore; + uint64_t Size; + bool DoPoison; + }; + typedef SmallVector AllocaPoisonCallVec; + typedef DenseMap AllocaPoisonCallsMapTy; + AllocaPoisonCallsMapTy AllocaPoisonCalls; + + // Used to store allocas which values come from. + typedef DenseMap AllocaForValueMapTy; + AllocaForValueMapTy AllocaForValue; + FunctionStackPoisoner(Function &F, AddressSanitizer &ASan) : F(F), ASan(ASan), DIB(*F.getParent()), C(ASan.C), IntptrTy(ASan.IntptrTy), IntptrPtrTy(PointerType::get(IntptrTy, 0)), @@ -354,9 +369,7 @@ /// \brief Collect Alloca instructions we want (and can) handle. void visitAllocaInst(AllocaInst &AI) { - if (AI.isArrayAllocation()) return; - if (!AI.isStaticAlloca()) return; - if (!AI.getAllocatedType()->isSized()) return; + if (!isInterestingAlloca(AI)) return; StackAlignment = std::max(StackAlignment, AI.getAlignment()); AllocaVec.push_back(&AI); @@ -364,9 +377,42 @@ TotalStackSize += AlignedSize; } + /// \brief Collect lifetime intrinsic calls to check for use-after-scope + /// errors. + void visitIntrinsicInst(IntrinsicInst &II) { + if (!ASan.CheckLifetime) return; + Intrinsic::ID ID = II.getIntrinsicID(); + if (ID != Intrinsic::lifetime_start && + ID != Intrinsic::lifetime_end) + return; + // Found lifetime intrinsic, add ASan instrumentation if necessary. + ConstantInt *Size = dyn_cast(II.getArgOperand(0)); + // If size argument is undefined, don't do anything. + if (Size->isMinusOne()) return; + // Check that size doesn't saturate uint64_t and can + // be stored in IntptrTy. + const uint64_t SizeValue = Size->getValue().getLimitedValue(); + if (SizeValue == ~0ULL || + !ConstantInt::isValueValidForType(IntptrTy, SizeValue)) + return; + // Find alloca instruction that corresponds to llvm.lifetime argument. + AllocaInst *AI = findAllocaForValue(II.getArgOperand(1)); + if (!AI) return; + bool DoPoison = (ID == Intrinsic::lifetime_end); + AllocaPoisonCall APC = {&II, SizeValue, DoPoison}; + AllocaPoisonCalls[AI].push_back(APC); + } + // ---------------------- Helpers. void initializeCallbacks(Module &M); + // Check if we want (and can) handle this alloca. + bool isInterestingAlloca(AllocaInst &AI) { + return (!AI.isArrayAllocation() && + AI.isStaticAlloca() && + AI.getAllocatedType()->isSized()); + } + uint64_t getAllocaSizeInBytes(AllocaInst *AI) { Type *Ty = AI->getAllocatedType(); uint64_t SizeInBytes = ASan.TD->getTypeAllocSize(Ty); @@ -380,16 +426,11 @@ uint64_t SizeInBytes = getAllocaSizeInBytes(AI); return getAlignedSize(SizeInBytes); } + /// Finds alloca where the value comes from. + AllocaInst *findAllocaForValue(Value *V); void poisonRedZones(const ArrayRef &AllocaVec, IRBuilder<> IRB, Value *ShadowBase, bool DoPoison); void poisonAlloca(Value *V, uint64_t Size, IRBuilder<> IRB, bool DoPoison); - /// Analyze lifetime intrinsics for given alloca. Use Value* instead of - /// AllocaInst* here, as we call this method after we merge all allocas into a - /// single one. Returns true if ASan added some instrumentation. - bool handleAllocaLifetime(Value *Alloca); - /// Analyze lifetime intrinsics for a specific value, casted from alloca. - /// Returns true if if ASan added some instrumentation. - bool handleValueLifetime(Value *V); }; } // namespace @@ -1154,7 +1195,8 @@ // Poison the full redzone at right. Ptr = IRB.CreateAdd(ShadowBase, ConstantInt::get(IntptrTy, Pos >> MappingScale())); - Value *Poison = i == AllocaVec.size() - 1 ? PoisonRight : PoisonMid; + bool LastAlloca = (i == AllocaVec.size() - 1); + Value *Poison = LastAlloca ? PoisonRight : PoisonMid; IRB.CreateStore(Poison, IRB.CreateIntToPtr(Ptr, RZPtrTy)); Pos += RedzoneSize(); @@ -1169,6 +1211,7 @@ bool DoStackMalloc = ASan.CheckUseAfterReturn && LocalStackSize <= kMaxStackMallocSize; + assert(AllocaVec.size() > 0); Instruction *InsBefore = AllocaVec[0]; IRBuilder<> IRB(InsBefore); @@ -1208,9 +1251,20 @@ AI->getType()); replaceDbgDeclareForAlloca(AI, NewAllocaPtr, DIB); AI->replaceAllUsesWith(NewAllocaPtr); - // Analyze lifetime intrinsics only for static allocas we handle. - if (ASan.CheckLifetime) - HavePoisonedAllocas |= handleAllocaLifetime(NewAllocaPtr); + // Insert poison calls for lifetime intrinsics for alloca. + if (ASan.CheckLifetime) { + AllocaPoisonCallsMapTy::iterator APCIter = AllocaPoisonCalls.find(AI); + if (APCIter != AllocaPoisonCalls.end()) { + const AllocaPoisonCallVec &APCVec = APCIter->second; + for (AllocaPoisonCallVec::const_iterator APCI = APCVec.begin(), + APCE = APCVec.end(); APCI != APCE; APCI++) { + const AllocaPoisonCall &APC = *APCI; + IRBuilder<> IRB(APC.InsBefore); + poisonAlloca(NewAllocaPtr, APC.Size, IRB, APC.DoPoison); + HavePoisonedAllocas |= APC.DoPoison; + } + } + } Pos += AlignedSize + RedzoneSize(); } assert(Pos == LocalStackSize); @@ -1278,62 +1332,37 @@ // variable may go in and out of scope several times, e.g. in loops). // (3) if we poisoned at least one %alloca in a function, // unpoison the whole stack frame at function exit. -bool FunctionStackPoisoner::handleAllocaLifetime(Value *Alloca) { - assert(ASan.CheckLifetime); - Type *AllocaType = Alloca->getType(); - Type *Int8PtrTy = Type::getInt8PtrTy(AllocaType->getContext()); - - bool Res = false; - // Typical code looks like this: - // %alloca = alloca , - // ... some code ... - // %val1 = bitcast * %alloca to i8* - // call void @llvm.lifetime.start(i64 , i8* %val1) - // ... more code ... - // %val2 = bitcast * %alloca to i8* - // call void @llvm.lifetime.start(i64 , i8* %val2) - // That is, to handle %alloca we must find all its casts to - // i8* values, and find lifetime instructions for these values. - if (AllocaType == Int8PtrTy) - Res |= handleValueLifetime(Alloca); - for (Value::use_iterator UI = Alloca->use_begin(), UE = Alloca->use_end(); - UI != UE; ++UI) { - if (UI->getType() != Int8PtrTy) continue; - if (UI->stripPointerCasts() != Alloca) continue; - Res |= handleValueLifetime(*UI); - } - return Res; -} -bool FunctionStackPoisoner::handleValueLifetime(Value *V) { - assert(ASan.CheckLifetime); - bool Res = false; - for (Value::use_iterator UI = V->use_begin(), UE = V->use_end(); UI != UE; - ++UI) { - IntrinsicInst *II = dyn_cast(*UI); - if (!II) continue; - Intrinsic::ID ID = II->getIntrinsicID(); - if (ID != Intrinsic::lifetime_start && - ID != Intrinsic::lifetime_end) - continue; - if (V != II->getArgOperand(1)) - continue; - // Found lifetime intrinsic, add ASan instrumentation if necessary. - ConstantInt *Size = dyn_cast(II->getArgOperand(0)); - // If size argument is undefined, don't do anything. - if (Size->isMinusOne()) - continue; - // Check that size doesn't saturate uint64_t and can - // be stored in IntptrTy. - const uint64_t SizeValue = Size->getValue().getLimitedValue(); - if (SizeValue == ~0ULL || - !ConstantInt::isValueValidForType(IntptrTy, SizeValue)) { - continue; +AllocaInst *FunctionStackPoisoner::findAllocaForValue(Value *V) { + if (AllocaInst *AI = dyn_cast(V)) + // We're intested only in allocas we can handle. + return isInterestingAlloca(*AI) ? AI : 0; + // See if we've already calculated alloca for a given value. + AllocaForValueMapTy::iterator I = AllocaForValue.find(V); + if (I != AllocaForValue.end()) + return I->second; + // Store 0 while we're calculating alloca for value V to avoid + // infinite recursion if the value references itself. + AllocaForValue[V] = 0; + AllocaInst *Res = 0; + if (CastInst *CI = dyn_cast(V)) + Res = findAllocaForValue(CI->getOperand(0)); + else if (PHINode *PN = dyn_cast(V)) { + for (unsigned i = 0, e = PN->getNumIncomingValues(); i != e; ++i) { + Value *IncValue = PN->getIncomingValue(i); + // Allow self-referencing phi-nodes. + if (IncValue == PN) continue; + AllocaInst *IncValueAI = findAllocaForValue(IncValue); + // AI for incoming values should exist and should all be equal. + if (IncValueAI == 0 || (Res != 0 && IncValueAI != Res)) { + Res = 0; + break; + } + if (Res == 0) + Res = IncValueAI; } - IRBuilder<> IRB(II); - bool DoPoison = (ID == Intrinsic::lifetime_end); - poisonAlloca(V, SizeValue, IRB, DoPoison); - Res = true; } + if (Res != 0) + AllocaForValue[V] = Res; return Res; } Index: test/Instrumentation/AddressSanitizer/lifetime.ll =================================================================== --- test/Instrumentation/AddressSanitizer/lifetime.ll +++ test/Instrumentation/AddressSanitizer/lifetime.ll @@ -31,7 +31,7 @@ %i.ptr = bitcast i32* %i to i8* call void @llvm.lifetime.start(i64 3, i8* %i.ptr) ; Memory is unpoisoned at llvm.lifetime.start - ; CHECK: %[[VAR:[^ ]*]] = ptrtoint i8* %i.ptr to i64 + ; CHECK: %[[VAR:[^ ]*]] = ptrtoint i32* %{{[^ ]+}} to i64 ; CHECK-NEXT: call void @__asan_unpoison_stack_memory(i64 %[[VAR]], i64 3) call void @llvm.lifetime.end(i64 4, i8* %i.ptr) call void @llvm.lifetime.end(i64 2, i8* %i.ptr) @@ -59,3 +59,26 @@ ; CHECK: ret void ret void } + +; Check that arguments of lifetime may come from phi nodes. +define void @phi_args(i1 %x) address_safety { + ; CHECK: @phi_args + +entry: + %i = alloca i64, align 4 + %i.ptr = bitcast i64* %i to i8* + call void @llvm.lifetime.start(i64 8, i8* %i.ptr) + ; CHECK: __asan_unpoison_stack_memory + br i1 %x, label %bb0, label %bb1 + +bb0: + %i.ptr2 = bitcast i64* %i to i8* + br label %bb1 + +bb1: + %i.phi = phi i8* [ %i.ptr, %entry ], [ %i.ptr2, %bb0 ] + call void @llvm.lifetime.end(i64 8, i8* %i.phi) + ; CHECK: __asan_poison_stack_memory + ; CHECK: ret void + ret void +}