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,14 @@ typedef DenseMap AllocaForValueMapTy; AllocaForValueMapTy AllocaForValue; + bool HasInlineAsm; + 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), + HasInlineAsm(false), EmptyInlineAsm(CallInst::Create(ASan.EmptyAsm)) {} bool runOnFunction() { if (!ClStack) return false; @@ -614,6 +622,10 @@ AllocaPoisonCallVec.push_back(APC); } + void visitCallInst(CallInst &CI) { + HasInlineAsm |= CI.isInlineAsm() && !CI.isIdenticalTo(EmptyInlineAsm.get()); + } + // ---------------------- Helpers. void initializeCallbacks(Module &M); @@ -649,6 +661,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 @@ -1497,9 +1513,9 @@ AsanStackMallocFunc[i] = checkInterfaceFunction( M.getOrInsertFunction(kAsanStackMallocNameTemplate + Suffix, IntptrTy, IntptrTy, IntptrTy, nullptr)); - AsanStackFreeFunc[i] = checkInterfaceFunction(M.getOrInsertFunction( - kAsanStackFreeNameTemplate + Suffix, IRB.getVoidTy(), IntptrTy, - IntptrTy, IntptrTy, nullptr)); + AsanStackFreeFunc[i] = checkInterfaceFunction( + M.getOrInsertFunction(kAsanStackFreeNameTemplate + Suffix, + IRB.getVoidTy(), IntptrTy, IntptrTy, nullptr)); } AsanPoisonStackMemoryFunc = checkInterfaceFunction( M.getOrInsertFunction(kAsanPoisonStackMemoryName, IRB.getVoidTy(), @@ -1571,6 +1587,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 +1651,61 @@ 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 && !HasInlineAsm; - 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); + // int8_t sp; + // void *FakeStack = __asan_option_detect_stack_use_after_return + // ? __asan_stack_malloc_N(LocalStackSize, &sp) + // : nullptr; + // void *LocalStackBase = (FakeStack) ? FakeStack : alloca(LocalStackSize); + AllocaInst *SPAlloca = IRB.CreateAlloca(IRB.getInt8Ty(), nullptr, "sp"); + assert(SPAlloca->isStaticAlloca()); + Value *SP = IRB.CreatePointerCast(SPAlloca, IntptrTy); + 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.CreateCall2(AsanStackMallocFunc[StackMallocIdx], + ConstantInt::get(IntptrTy, LocalStackSize), SP); + 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 +1762,17 @@ BasePlus0); if (DoStackMalloc) { assert(StackMallocIdx >= 0); - // if LocalStackBase != OrigStackBase: + // if 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 // else - // __asan_stack_free_N(LocalStackBase, OrigStackBase) + // __asan_stack_free_N(LocalStackBase, LocalStackSize) // else // - Value *Cmp = IRBRet.CreateICmpNE(LocalStackBase, OrigStackBase); + Value *Cmp = IRBRet.CreateICmpEQ(LocalStackBase, FakeStack); TerminatorInst *ThenTerm, *ElseTerm; SplitBlockAndInsertIfThenElse(Cmp, Ret, &ThenTerm, &ElseTerm); @@ -1726,9 +1791,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], LocalStackBase, + ConstantInt::get(IntptrTy, LocalStackSize)); } IRBuilder<> IRBElse(ElseTerm); @@ -1736,7 +1800,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/basic.ll =================================================================== --- test/Instrumentation/AddressSanitizer/basic.ll +++ test/Instrumentation/AddressSanitizer/basic.ll @@ -70,7 +70,7 @@ ret void } -; Check that asan leaves just one alloca. +; Check that asan leaves just two allocas (one for sp, another for locals). declare void @alloca_test_use([10 x i8]*) define void @alloca_test() sanitize_address { @@ -78,14 +78,17 @@ %x = alloca [10 x i8], align 1 %y = alloca [10 x i8], align 1 %z = alloca [10 x i8], align 1 + %zz = alloca [10 x i8], align 1 call void @alloca_test_use([10 x i8]* %x) call void @alloca_test_use([10 x i8]* %y) call void @alloca_test_use([10 x i8]* %z) + call void @alloca_test_use([10 x i8]* %zz) ret void } ; CHECK-LABEL: define void @alloca_test() ; CHECK: = alloca +; CHECK: = alloca i8 ; CHECK-NOT: = alloca ; CHECK: ret void Index: test/Instrumentation/AddressSanitizer/stack_dynamic_alloca.ll =================================================================== --- /dev/null +++ test/Instrumentation/AddressSanitizer/stack_dynamic_alloca.ll @@ -0,0 +1,32 @@ +; 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: %sp = alloca i8 +; CHECK: load i32* @__asan_option_detect_stack_use_after_return + +; CHECK: