static const char *const kAsanInitName = "__asan_init_v4";
static const char *const kAsanCovModuleInitName = "__sanitizer_cov_module_init";
static const char *const kAsanCovName = "__sanitizer_cov";
+static const char *const kAsanCovIndirCallName = "__sanitizer_cov_indir_call16";
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("Handle global objects"), cl::Hidden, cl::init(true));
static cl::opt<int> ClCoverage("asan-coverage",
cl::desc("ASan coverage. 0: none, 1: entry block, 2: all blocks, "
- "3: all blocks and critical edges"),
+ "3: all blocks and critical edges, "
+ "4: above plus indirect calls"),
cl::Hidden, cl::init(false));
static cl::opt<int> ClCoverageBlockThreshold("asan-coverage-block-threshold",
cl::desc("Add coverage instrumentation only to the entry block if there "
bool LooksLikeCodeInBug11395(Instruction *I);
bool GlobalIsLinkerInitialized(GlobalVariable *G);
- bool InjectCoverage(Function &F, ArrayRef<BasicBlock*> AllBlocks);
+ void InjectCoverageForIndirectCalls(Function &F,
+ ArrayRef<Instruction *> IndirCalls);
+ bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks,
+ ArrayRef<Instruction *> IndirCalls);
void InjectCoverageAtBlock(Function &F, BasicBlock &BB);
LLVMContext *C;
Function *AsanInitFunction;
Function *AsanHandleNoReturnFunc;
Function *AsanCovFunction;
+ Function *AsanCovIndirCallFunction;
Function *AsanPtrCmpFunction, *AsanPtrSubFunction;
// This array is indexed by AccessIsWrite and log2(AccessSize).
Function *AsanErrorCallback[2][kNumberOfAccessSizes];
M.getOrInsertFunction(kAsanHandleNoReturnName, IRB.getVoidTy(), NULL));
AsanCovFunction = checkInterfaceFunction(M.getOrInsertFunction(
kAsanCovName, IRB.getVoidTy(), NULL));
+ AsanCovIndirCallFunction = checkInterfaceFunction(M.getOrInsertFunction(
+ kAsanCovIndirCallName, IRB.getVoidTy(), IntptrTy, IntptrTy, NULL));
+
AsanPtrCmpFunction = checkInterfaceFunction(M.getOrInsertFunction(
kAsanPtrCmp, IRB.getVoidTy(), IntptrTy, IntptrTy, NULL));
AsanPtrSubFunction = checkInterfaceFunction(M.getOrInsertFunction(
// a) get the functionality to users earlier and
// b) collect usage statistics to help improve Clang coverage design.
bool AddressSanitizer::InjectCoverage(Function &F,
- ArrayRef<BasicBlock *> AllBlocks) {
+ ArrayRef<BasicBlock *> AllBlocks,
+ ArrayRef<Instruction*> IndirCalls) {
if (!ClCoverage) return false;
if (ClCoverage == 1 ||
for (auto BB : AllBlocks)
InjectCoverageAtBlock(F, *BB);
}
+ InjectCoverageForIndirectCalls(F, IndirCalls);
return true;
}
+// On every indirect call we call a run-time function
+// __sanitizer_cov_indir_call* with two parameters:
+// - callee address,
+// - global cache array that contains kCacheSize pointers (zero-initialed).
+// The cache is used to speed up recording the caller-callee pairs.
+// The address of the caller is passed implicitly via caller PC.
+// kCacheSize is encoded in the name of the run-time function.
+void AddressSanitizer::InjectCoverageForIndirectCalls(
+ Function &F, ArrayRef<Instruction *> IndirCalls) {
+ if (ClCoverage < 4 || IndirCalls.empty()) return;
+ const int kCacheSize = 16;
+ const int kCacheAlignment = 64; // Align for better performance.
+ Type *Ty = ArrayType::get(IntptrTy, kCacheSize);
+ GlobalVariable *CalleeCache =
+ new GlobalVariable(*F.getParent(), Ty, false, GlobalValue::PrivateLinkage,
+ Constant::getNullValue(Ty), "__asan_gen_callee_cache");
+ CalleeCache->setAlignment(kCacheAlignment);
+ for (auto I : IndirCalls) {
+ IRBuilder<> IRB(I);
+ CallSite CS(I);
+ IRB.CreateCall2(AsanCovIndirCallFunction,
+ IRB.CreatePointerCast(CS.getCalledValue(), IntptrTy),
+ IRB.CreatePointerCast(CalleeCache, IntptrTy));
+ }
+}
+
bool AddressSanitizer::runOnFunction(Function &F) {
if (&F == AsanCtorFunction) return false;
if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) return false;
SmallVector<Instruction*, 8> NoReturnCalls;
SmallVector<BasicBlock*, 16> AllBlocks;
SmallVector<Instruction*, 16> PointerComparisonsOrSubtracts;
+ SmallVector<Instruction*, 8> IndirCalls;
int NumAllocas = 0;
bool IsWrite;
unsigned Alignment;
TempsToInstrument.clear();
if (CS.doesNotReturn())
NoReturnCalls.push_back(CS.getInstruction());
+ if (ClCoverage >= 4 && !CS.getCalledFunction())
+ IndirCalls.push_back(&Inst);
}
continue;
}
bool res = NumInstrumented > 0 || ChangedStack || !NoReturnCalls.empty();
- if (InjectCoverage(F, AllBlocks))
+ if (InjectCoverage(F, AllBlocks, IndirCalls))
res = true;
DEBUG(dbgs() << "ASAN done instrumenting: " << res << " " << F << "\n");
; RUN: opt < %s -asan -asan-module -asan-coverage=2 -asan-coverage-block-threshold=10 -S | FileCheck %s --check-prefix=CHECK2
; RUN: opt < %s -asan -asan-module -asan-coverage=2 -asan-coverage-block-threshold=1 -S | FileCheck %s --check-prefix=CHECK1
; RUN: opt < %s -asan -asan-module -asan-coverage=3 -asan-coverage-block-threshold=10 -S | FileCheck %s --check-prefix=CHECK3
+; RUN: opt < %s -asan -asan-module -asan-coverage=4 -S | FileCheck %s --check-prefix=CHECK4
; RUN: opt < %s -asan -asan-module -asan-coverage=0 -asan-globals=0 -S | \
; RUN: FileCheck %s --check-prefix=CHECK0
; CHECK1-LABEL: define internal void @asan.module_ctor
; CHECK1-NOT: ret
-; CHECK1: call void @__sanitizer_cov_module_init(i64 1)
+; CHECK1: call void @__sanitizer_cov_module_init(i64 2)
; CHECK1: ret
; CHECK2-LABEL: define internal void @asan.module_ctor
; CHECK2-NOT: ret
-; CHECK2: call void @__sanitizer_cov_module_init(i64 3)
+; CHECK2: call void @__sanitizer_cov_module_init(i64 4)
; CHECK2: ret
; CHECK3-LABEL: define void @foo
; CHECK3-NOT: call void @__sanitizer_cov
; CHECK3: ret void
+
+%struct.StructWithVptr = type { i32 (...)** }
+
+define void @CallViaVptr(%struct.StructWithVptr* %foo) uwtable sanitize_address {
+entry:
+ %0 = bitcast %struct.StructWithVptr* %foo to void (%struct.StructWithVptr*)***
+ %vtable = load void (%struct.StructWithVptr*)*** %0, align 8
+ %1 = load void (%struct.StructWithVptr*)** %vtable, align 8
+ tail call void %1(%struct.StructWithVptr* %foo)
+ ret void
+}
+
+; CHECK4-LABEL: define void @CallViaVptr
+; CHECK4: call void @__sanitizer_cov_indir_call16
+; CHECK4: ret void