diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -556,6 +556,22 @@ } } +// For a ret instruction followed by a musttail call, we cannot insert anything +// in between. Instead we use the musttail call instruction as the insertion +// point. +static Instruction *adjustForMusttailCall(Instruction *I) { + ReturnInst *RI = dyn_cast(I); + if (!RI) + return I; + Instruction *Prev = RI->getPrevNode(); + if (BitCastInst *BCI = dyn_cast_or_null(Prev)) + Prev = BCI->getPrevNode(); + if (CallInst *CI = dyn_cast_or_null(Prev)) + if (CI->isMustTailCall()) + return CI; + return RI; +} + namespace { /// Module analysis for getting various metadata about the module. @@ -999,10 +1015,11 @@ // Unpoison dynamic allocas redzones. void unpoisonDynamicAllocas() { - for (auto &Ret : RetVec) - unpoisonDynamicAllocasBeforeInst(Ret, DynamicAllocaLayout); + for (Instruction *Ret : RetVec) + unpoisonDynamicAllocasBeforeInst(adjustForMusttailCall(Ret), + DynamicAllocaLayout); - for (auto &StackRestoreInst : StackRestoreVec) + for (Instruction *StackRestoreInst : StackRestoreVec) unpoisonDynamicAllocasBeforeInst(StackRestoreInst, StackRestoreInst->getOperand(0)); } @@ -3303,8 +3320,9 @@ SmallVector ShadowAfterReturn; // (Un)poison the stack before all ret instructions. - for (auto Ret : RetVec) { - IRBuilder<> IRBRet(Ret); + for (Instruction *Ret : RetVec) { + Instruction *Adjusted = adjustForMusttailCall(Ret); + IRBuilder<> IRBRet(Adjusted); // Mark the current frame as retired. IRBRet.CreateStore(ConstantInt::get(IntptrTy, kRetiredStackFrameMagic), BasePlus0); @@ -3323,7 +3341,7 @@ Value *Cmp = IRBRet.CreateICmpNE(FakeStack, Constant::getNullValue(IntptrTy)); Instruction *ThenTerm, *ElseTerm; - SplitBlockAndInsertIfThenElse(Cmp, Ret, &ThenTerm, &ElseTerm); + SplitBlockAndInsertIfThenElse(Cmp, Adjusted, &ThenTerm, &ElseTerm); IRBuilder<> IRBPoison(ThenTerm); if (StackMallocIdx <= 4) { diff --git a/llvm/test/Instrumentation/AddressSanitizer/musttail.ll b/llvm/test/Instrumentation/AddressSanitizer/musttail.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/AddressSanitizer/musttail.ll @@ -0,0 +1,35 @@ +; To test that asan does not break the musttail call contract. +; +; RUN: opt < %s -asan -asan-module -S -enable-new-pm=0 | FileCheck %s +; RUN: opt < %s -passes='asan-pipeline' -S | FileCheck %s + +define internal i32 @foo(i32* %p) sanitize_address { + %rv = load i32, i32* %p + ret i32 %rv +} + +declare void @alloca_test_use([10 x i8]*) +define i32 @call_foo(i32* %a) sanitize_address { + %x = alloca [10 x i8], align 1 + call void @alloca_test_use([10 x i8]* %x) + %r = musttail call i32 @foo(i32* %a) + ret i32 %r +} + +; CHECK-LABEL: define i32 @call_foo(i32* %a) +; CHECK: %r = musttail call i32 @foo(i32* %a) +; CHECK-NEXT: ret i32 %r + + +define i32 @call_foo_cast(i32* %a) sanitize_address { + %x = alloca [10 x i8], align 1 + call void @alloca_test_use([10 x i8]* %x) + %r = musttail call i32 @foo(i32* %a) + %t = bitcast i32 %r to i32 + ret i32 %t +} + +; CHECK-LABEL: define i32 @call_foo_cast(i32* %a) +; CHECK: %r = musttail call i32 @foo(i32* %a) +; CHECK-NEXT: %t = bitcast i32 %r to i32 +; CHECK-NEXT: ret i32 %t