Index: lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -83,7 +83,7 @@ "__asan_unregister_globals"; static const char *const kAsanPoisonGlobalsName = "__asan_before_dynamic_init"; static const char *const kAsanUnpoisonGlobalsName = "__asan_after_dynamic_init"; -static const char *const kAsanInitName = "__asan_init_v4"; +static const char *const kAsanInitName = "__asan_init_v5"; static const char *const kAsanPtrCmp = "__sanitizer_ptr_cmp"; static const char *const kAsanPtrSub = "__sanitizer_ptr_sub"; static const char *const kAsanHandleNoReturnName = "__asan_handle_no_return"; @@ -183,6 +183,11 @@ cl::desc("Use llvm.lifetime intrinsics to insert extra checks"), cl::Hidden, cl::init(false)); +static cl::opt ClDynamicAllocaStack( + "asan-stack-dynamic-alloca", + cl::desc("Use dynamic alloca to represent stack variables"), cl::Hidden, + cl::init(false)); + // Debug flags. static cl::opt ClDebug("asan-debug", cl::desc("debug"), cl::Hidden, cl::init(0)); @@ -493,11 +498,15 @@ typedef DenseMap AllocaForValueMapTy; AllocaForValueMapTy AllocaForValue; + bool HasNonEmptyInlineAsm; + std::unique_ptr EmptyInlineAsm; + FunctionStackPoisoner(Function &F, AddressSanitizer &ASan) : F(F), ASan(ASan), DIB(*F.getParent()), C(ASan.C), IntptrTy(ASan.IntptrTy), IntptrPtrTy(PointerType::get(IntptrTy, 0)), - Mapping(ASan.Mapping), - StackAlignment(1 << Mapping.Scale) {} + Mapping(ASan.Mapping), StackAlignment(1 << Mapping.Scale), + HasNonEmptyInlineAsm(false), + EmptyInlineAsm(CallInst::Create(ASan.EmptyAsm)) {} bool runOnFunction() { if (!ClStack) return false; @@ -614,6 +623,11 @@ AllocaPoisonCallVec.push_back(APC); } + void visitCallInst(CallInst &CI) { + HasNonEmptyInlineAsm |= + CI.isInlineAsm() && !CI.isIdenticalTo(EmptyInlineAsm.get()); + } + // ---------------------- Helpers. void initializeCallbacks(Module &M); @@ -649,6 +663,10 @@ void SetShadowToStackAfterReturnInlined(IRBuilder<> &IRB, Value *ShadowBase, int Size); + Value *createAllocaForLayout(IRBuilder<> &IRB, const ASanStackFrameLayout &L, + bool Dynamic); + PHINode *createPHI(IRBuilder<> &IRB, Value *Cond, Value *ValueIfTrue, + Instruction *ThenTerm, Value *ValueIfFalse); }; } // namespace @@ -1494,12 +1512,11 @@ IRBuilder<> IRB(*C); for (int i = 0; i <= kMaxAsanStackMallocSizeClass; i++) { std::string Suffix = itostr(i); - AsanStackMallocFunc[i] = checkInterfaceFunction( - M.getOrInsertFunction(kAsanStackMallocNameTemplate + Suffix, IntptrTy, - IntptrTy, IntptrTy, nullptr)); - AsanStackFreeFunc[i] = checkInterfaceFunction(M.getOrInsertFunction( - kAsanStackFreeNameTemplate + Suffix, IRB.getVoidTy(), IntptrTy, - IntptrTy, IntptrTy, nullptr)); + AsanStackMallocFunc[i] = checkInterfaceFunction(M.getOrInsertFunction( + kAsanStackMallocNameTemplate + Suffix, IntptrTy, IntptrTy, nullptr)); + AsanStackFreeFunc[i] = checkInterfaceFunction( + M.getOrInsertFunction(kAsanStackFreeNameTemplate + Suffix, + IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr)); } AsanPoisonStackMemoryFunc = checkInterfaceFunction( M.getOrInsertFunction(kAsanPoisonStackMemoryName, IRB.getVoidTy(), @@ -1571,6 +1588,36 @@ return DebugLoc(); } +PHINode *FunctionStackPoisoner::createPHI(IRBuilder<> &IRB, Value *Cond, + Value *ValueIfTrue, + Instruction *ThenTerm, + Value *ValueIfFalse) { + PHINode *PHI = IRB.CreatePHI(IntptrTy, 2); + BasicBlock *CondBlock = cast(Cond)->getParent(); + PHI->addIncoming(ValueIfFalse, CondBlock); + BasicBlock *ThenBlock = ThenTerm->getParent(); + PHI->addIncoming(ValueIfTrue, ThenBlock); + return PHI; +} + +Value *FunctionStackPoisoner::createAllocaForLayout( + IRBuilder<> &IRB, const ASanStackFrameLayout &L, bool Dynamic) { + AllocaInst *Alloca; + if (Dynamic) { + Alloca = IRB.CreateAlloca(IRB.getInt8Ty(), + ConstantInt::get(IRB.getInt64Ty(), L.FrameSize), + "MyAlloca"); + } else { + Alloca = IRB.CreateAlloca(ArrayType::get(IRB.getInt8Ty(), L.FrameSize), + nullptr, "MyAlloca"); + assert(Alloca->isStaticAlloca()); + } + assert((ClRealignStack & (ClRealignStack - 1)) == 0); + size_t FrameAlignment = std::max(L.FrameAlignment, (size_t)ClRealignStack); + Alloca->setAlignment(FrameAlignment); + return IRB.CreatePointerCast(Alloca, IntptrTy); +} + void FunctionStackPoisoner::poisonStack() { assert(AllocaVec.size() > 0 || DynamicAllocaVec.size() > 0); @@ -1605,42 +1652,56 @@ uint64_t LocalStackSize = L.FrameSize; bool DoStackMalloc = ClUseAfterReturn && LocalStackSize <= kMaxStackMallocSize; + // Don't do dynamic alloca in presence of inline asm: too often it + // makes assumptions on which registers are available. + bool DoDynamicAlloca = ClDynamicAllocaStack && !HasNonEmptyInlineAsm; - Type *ByteArrayTy = ArrayType::get(IRB.getInt8Ty(), LocalStackSize); - AllocaInst *MyAlloca = - new AllocaInst(ByteArrayTy, "MyAlloca", InsBefore); - MyAlloca->setDebugLoc(EntryDebugLocation); - assert((ClRealignStack & (ClRealignStack - 1)) == 0); - size_t FrameAlignment = std::max(L.FrameAlignment, (size_t)ClRealignStack); - MyAlloca->setAlignment(FrameAlignment); - assert(MyAlloca->isStaticAlloca()); - Value *OrigStackBase = IRB.CreatePointerCast(MyAlloca, IntptrTy); - Value *LocalStackBase = OrigStackBase; + Value *StaticAlloca = + DoDynamicAlloca ? nullptr : createAllocaForLayout(IRB, L, false); + + Value *FakeStack; + Value *LocalStackBase; if (DoStackMalloc) { - // LocalStackBase = OrigStackBase - // if (__asan_option_detect_stack_use_after_return) - // LocalStackBase = __asan_stack_malloc_N(LocalStackBase, OrigStackBase); - StackMallocIdx = StackMallocSizeClass(LocalStackSize); - assert(StackMallocIdx <= kMaxAsanStackMallocSizeClass); + // void *FakeStack = __asan_option_detect_stack_use_after_return + // ? __asan_stack_malloc_N(LocalStackSize) + // : nullptr; + // void *LocalStackBase = (FakeStack) ? FakeStack : alloca(LocalStackSize); Constant *OptionDetectUAR = F.getParent()->getOrInsertGlobal( kAsanOptionDetectUAR, IRB.getInt32Ty()); - Value *Cmp = IRB.CreateICmpNE(IRB.CreateLoad(OptionDetectUAR), - Constant::getNullValue(IRB.getInt32Ty())); - Instruction *Term = SplitBlockAndInsertIfThen(Cmp, InsBefore, false); - BasicBlock *CmpBlock = cast(Cmp)->getParent(); + Value *UARIsEnabled = + IRB.CreateICmpNE(IRB.CreateLoad(OptionDetectUAR), + Constant::getNullValue(IRB.getInt32Ty())); + Instruction *Term = + SplitBlockAndInsertIfThen(UARIsEnabled, InsBefore, false); IRBuilder<> IRBIf(Term); IRBIf.SetCurrentDebugLocation(EntryDebugLocation); - LocalStackBase = IRBIf.CreateCall2( - AsanStackMallocFunc[StackMallocIdx], - ConstantInt::get(IntptrTy, LocalStackSize), OrigStackBase); - BasicBlock *SetBlock = cast(LocalStackBase)->getParent(); + StackMallocIdx = StackMallocSizeClass(LocalStackSize); + assert(StackMallocIdx <= kMaxAsanStackMallocSizeClass); + Value *FakeStackValue = + IRBIf.CreateCall(AsanStackMallocFunc[StackMallocIdx], + ConstantInt::get(IntptrTy, LocalStackSize)); + IRB.SetInsertPoint(InsBefore); + IRB.SetCurrentDebugLocation(EntryDebugLocation); + FakeStack = createPHI(IRB, UARIsEnabled, FakeStackValue, Term, + ConstantInt::get(IntptrTy, 0)); + + Value *NoFakeStack = + IRB.CreateICmpEQ(FakeStack, Constant::getNullValue(IntptrTy)); + Term = SplitBlockAndInsertIfThen(NoFakeStack, InsBefore, false); + IRBIf.SetInsertPoint(Term); + IRBIf.SetCurrentDebugLocation(EntryDebugLocation); + Value *AllocaValue = + DoDynamicAlloca ? createAllocaForLayout(IRBIf, L, true) : StaticAlloca; IRB.SetInsertPoint(InsBefore); IRB.SetCurrentDebugLocation(EntryDebugLocation); - PHINode *Phi = IRB.CreatePHI(IntptrTy, 2); - Phi->addIncoming(OrigStackBase, CmpBlock); - Phi->addIncoming(LocalStackBase, SetBlock); - LocalStackBase = Phi; + LocalStackBase = createPHI(IRB, NoFakeStack, AllocaValue, Term, FakeStack); + } else { + // void *FakeStack = nullptr; + // void *LocalStackBase = alloca(LocalStackSize); + FakeStack = ConstantInt::get(IntptrTy, 0); + LocalStackBase = + DoDynamicAlloca ? createAllocaForLayout(IRB, L, true) : StaticAlloca; } // Insert poison calls for lifetime intrinsics for alloca. @@ -1697,17 +1758,18 @@ BasePlus0); if (DoStackMalloc) { assert(StackMallocIdx >= 0); - // if LocalStackBase != OrigStackBase: + // if FakeStack != 0 // LocalStackBase == FakeStack // // In use-after-return mode, poison the whole stack frame. // if StackMallocIdx <= 4 // // For small sizes inline the whole thing: // memset(ShadowBase, kAsanStackAfterReturnMagic, ShadowSize); - // **SavedFlagPtr(LocalStackBase) = 0 + // **SavedFlagPtr(FakeStack) = 0 // else - // __asan_stack_free_N(LocalStackBase, OrigStackBase) + // __asan_stack_free_N(FakeStack, LocalStackSize) // else // - Value *Cmp = IRBRet.CreateICmpNE(LocalStackBase, OrigStackBase); + Value *Cmp = + IRB.CreateICmpNE(FakeStack, Constant::getNullValue(IntptrTy)); TerminatorInst *ThenTerm, *ElseTerm; SplitBlockAndInsertIfThenElse(Cmp, Ret, &ThenTerm, &ElseTerm); @@ -1717,7 +1779,7 @@ SetShadowToStackAfterReturnInlined(IRBPoison, ShadowBase, ClassSize >> Mapping.Scale); Value *SavedFlagPtrPtr = IRBPoison.CreateAdd( - LocalStackBase, + FakeStack, ConstantInt::get(IntptrTy, ClassSize - ASan.LongSize / 8)); Value *SavedFlagPtr = IRBPoison.CreateLoad( IRBPoison.CreateIntToPtr(SavedFlagPtrPtr, IntptrPtrTy)); @@ -1726,9 +1788,8 @@ IRBPoison.CreateIntToPtr(SavedFlagPtr, IRBPoison.getInt8PtrTy())); } else { // For larger frames call __asan_stack_free_*. - IRBPoison.CreateCall3(AsanStackFreeFunc[StackMallocIdx], LocalStackBase, - ConstantInt::get(IntptrTy, LocalStackSize), - OrigStackBase); + IRBPoison.CreateCall2(AsanStackFreeFunc[StackMallocIdx], FakeStack, + ConstantInt::get(IntptrTy, LocalStackSize)); } IRBuilder<> IRBElse(ElseTerm); @@ -1736,7 +1797,6 @@ } else if (HavePoisonedAllocas) { // If we poisoned some allocas in llvm.lifetime analysis, // unpoison whole stack frame now. - assert(LocalStackBase == OrigStackBase); poisonAlloca(LocalStackBase, LocalStackSize, IRBRet, false); } else { poisonRedZones(L.ShadowBytes, IRBRet, ShadowBase, false); Index: test/Instrumentation/AddressSanitizer/stack_dynamic_alloca.ll =================================================================== --- /dev/null +++ test/Instrumentation/AddressSanitizer/stack_dynamic_alloca.ll @@ -0,0 +1,31 @@ +; RUN: opt < %s -asan -asan-module -asan-stack-dynamic-alloca \ +; RUN: -asan-use-after-return -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-S128" +target triple = "x86_64-unknown-linux-gnu" + +define void @Func1() sanitize_address { +entry: +; CHECK-LABEL: Func1 + +; CHECK: entry: +; CHECK: load i32* @__asan_option_detect_stack_use_after_return + +; CHECK: