"__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";
cl::desc("Use llvm.lifetime intrinsics to insert extra checks"),
cl::Hidden, cl::init(false));
+static cl::opt<bool> ClDynamicAllocaStack(
+ "asan-stack-dynamic-alloca",
+ cl::desc("Use dynamic alloca to represent stack variables"), cl::Hidden,
+ cl::init(false));
+
// Debug flags.
static cl::opt<int> ClDebug("asan-debug", cl::desc("debug"), cl::Hidden,
cl::init(0));
typedef DenseMap<Value*, AllocaInst*> AllocaForValueMapTy;
AllocaForValueMapTy AllocaForValue;
+ bool HasNonEmptyInlineAsm;
+ std::unique_ptr<CallInst> EmptyInlineAsm;
+
FunctionStackPoisoner(Function &F, AddressSanitizer &ASan)
: F(F), ASan(ASan), DIB(*F.getParent(), /*AllowUnresolved*/ false),
C(ASan.C), IntptrTy(ASan.IntptrTy),
IntptrPtrTy(PointerType::get(IntptrTy, 0)), Mapping(ASan.Mapping),
- StackAlignment(1 << Mapping.Scale) {}
+ StackAlignment(1 << Mapping.Scale), HasNonEmptyInlineAsm(false),
+ EmptyInlineAsm(CallInst::Create(ASan.EmptyAsm)) {}
bool runOnFunction() {
if (!ClStack) return false;
AllocaPoisonCallVec.push_back(APC);
}
+ void visitCallInst(CallInst &CI) {
+ HasNonEmptyInlineAsm |=
+ CI.isInlineAsm() && !CI.isIdenticalTo(EmptyInlineAsm.get());
+ }
+
// ---------------------- Helpers.
void initializeCallbacks(Module &M);
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
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(),
return DebugLoc();
}
+PHINode *FunctionStackPoisoner::createPHI(IRBuilder<> &IRB, Value *Cond,
+ Value *ValueIfTrue,
+ Instruction *ThenTerm,
+ Value *ValueIfFalse) {
+ PHINode *PHI = IRB.CreatePHI(IntptrTy, 2);
+ BasicBlock *CondBlock = cast<Instruction>(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);
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<Instruction>(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<Instruction>(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.
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
// <This is not a fake stack; unpoison the redzones>
- Value *Cmp = IRBRet.CreateICmpNE(LocalStackBase, OrigStackBase);
+ Value *Cmp =
+ IRBRet.CreateICmpNE(FakeStack, Constant::getNullValue(IntptrTy));
TerminatorInst *ThenTerm, *ElseTerm;
SplitBlockAndInsertIfThenElse(Cmp, Ret, &ThenTerm, &ElseTerm);
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));
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);
} 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);