[sanitizer-coverage/libFuzzer] make the guards for trace-pc 32-bit; create one array of guards per function, instead of one guard per BB. reorganize the code so that trace-pc-guard does not create unneeded globals

llvm-svn: 282735
This commit is contained in:
Kostya Serebryany 2016-09-29 17:43:24 +00:00
parent ba72b95f7b
commit a9b0dd0e51
7 changed files with 107 additions and 80 deletions

View File

@ -334,7 +334,7 @@ on every edge:
if (guard_variable)
__sanitizer_cov_trace_pc_guard(&guard_variable)
Every edge will have its own `guard_variable` (uintptr_t).
Every edge will have its own `guard_variable` (uint32_t).
The compler will also insert a module constructor that will call
@ -342,7 +342,7 @@ The compler will also insert a module constructor that will call
// The guards are [start, stop).
// This function may be called multiple times with the same values of start/stop.
__sanitizer_cov_trace_pc_guard_init(uintptr_t *start, uintptr_t *stop);
__sanitizer_cov_trace_pc_guard_init(uint32_t_t *start, uint32_t *stop);
Similarly to `trace-pc,indirect-calls`, with `trace-pc-guards,indirect-calls`
``__sanitizer_cov_trace_pc_indirect(void *callee)`` will be inserted on every indirect call.

View File

@ -500,7 +500,8 @@ void Fuzzer::ExecuteCallback(const uint8_t *Data, size_t Size) {
UnitStartTime = system_clock::now();
ResetCounters(); // Reset coverage right before the callback.
TPC.ResetMaps();
TPC.ResetGuards();
if (Options.UseCounters)
TPC.ResetGuards();
int Res = CB(DataCopy, Size);
UnitStopTime = system_clock::now();
(void)Res;

View File

@ -20,8 +20,8 @@ namespace fuzzer {
TracePC TPC;
void TracePC::HandleTrace(uintptr_t *Guard, uintptr_t PC) {
uintptr_t Idx = *Guard;
void TracePC::HandleTrace(uint32_t *Guard, uintptr_t PC) {
uint32_t Idx = *Guard;
if (!Idx) return;
uint8_t *CounterPtr = &Counters[Idx % kNumCounters];
uint8_t Counter = *CounterPtr;
@ -43,10 +43,10 @@ void TracePC::HandleTrace(uintptr_t *Guard, uintptr_t PC) {
}
}
void TracePC::HandleInit(uintptr_t *Start, uintptr_t *Stop) {
void TracePC::HandleInit(uint32_t *Start, uint32_t *Stop) {
if (Start == Stop || *Start) return;
assert(NumModules < sizeof(Modules) / sizeof(Modules[0]));
for (uintptr_t *P = Start; P < Stop; P++)
for (uint32_t *P = Start; P < Stop; P++)
*P = ++NumGuards;
Modules[NumModules].Start = Start;
Modules[NumModules].Stop = Stop;
@ -61,9 +61,9 @@ void TracePC::PrintModuleInfo() {
}
void TracePC::ResetGuards() {
uintptr_t N = 0;
uint32_t N = 0;
for (size_t M = 0; M < NumModules; M++)
for (uintptr_t *X = Modules[M].Start; X < Modules[M].Stop; X++)
for (uint32_t *X = Modules[M].Start; X < Modules[M].Stop; X++)
*X = ++N;
assert(N == NumGuards);
}
@ -138,13 +138,13 @@ void TracePC::PrintFeatureSet() {
extern "C" {
__attribute__((visibility("default")))
void __sanitizer_cov_trace_pc_guard(uintptr_t *Guard) {
void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) {
uintptr_t PC = (uintptr_t)__builtin_return_address(0);
fuzzer::TPC.HandleTrace(Guard, PC);
}
__attribute__((visibility("default")))
void __sanitizer_cov_trace_pc_guard_init(uintptr_t *Start, uintptr_t *Stop) {
void __sanitizer_cov_trace_pc_guard_init(uint32_t *Start, uint32_t *Stop) {
fuzzer::TPC.HandleInit(Start, Stop);
}

View File

@ -19,8 +19,8 @@ namespace fuzzer {
class TracePC {
public:
void HandleTrace(uintptr_t *guard, uintptr_t PC);
void HandleInit(uintptr_t *start, uintptr_t *stop);
void HandleTrace(uint32_t *guard, uintptr_t PC);
void HandleInit(uint32_t *start, uint32_t *stop);
void HandleCallerCallee(uintptr_t Caller, uintptr_t Callee);
void HandleValueProfile(size_t Value) { ValueProfileMap.AddValue(Value); }
size_t GetTotalPCCoverage() { return TotalPCCoverage; }
@ -71,7 +71,7 @@ private:
}
struct Module {
uintptr_t *Start, *Stop;
uint32_t *Start, *Stop;
};
Module Modules[4096];

View File

@ -207,8 +207,10 @@ private:
void InjectTraceForSwitch(Function &F,
ArrayRef<Instruction *> SwitchTraceTargets);
bool InjectCoverage(Function &F, ArrayRef<BasicBlock *> AllBlocks);
void CreateFunctionGuardArray(size_t NumGuards, Function &F);
void SetNoSanitizeMetadata(Instruction *I);
void InjectCoverageAtBlock(Function &F, BasicBlock &BB, bool UseCalls);
void InjectCoverageAtBlock(Function &F, BasicBlock &BB, size_t Idx,
bool UseCalls);
unsigned NumberOfInstrumentedBlocks() {
return SanCovFunction->getNumUses() +
SanCovWithCheckFunction->getNumUses() + SanCovTraceBB->getNumUses() +
@ -223,12 +225,13 @@ private:
Function *SanCovTraceGepFunction;
Function *SanCovTraceSwitchFunction;
InlineAsm *EmptyAsm;
Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy;
Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy;
Module *CurModule;
LLVMContext *C;
const DataLayout *DL;
GlobalVariable *GuardArray;
GlobalVariable *FunctionGuardArray; // for trace-pc-guard.
GlobalVariable *EightBitCounterArray;
bool HasSancovGuardsSection;
@ -249,9 +252,10 @@ bool SanitizerCoverageModule::runOnModule(Module &M) {
Type *VoidTy = Type::getVoidTy(*C);
IRBuilder<> IRB(*C);
Type *Int8PtrTy = PointerType::getUnqual(IRB.getInt8Ty());
Type *Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty());
Int64PtrTy = PointerType::getUnqual(IRB.getInt64Ty());
Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty());
Int64Ty = IRB.getInt64Ty();
Int32Ty = IRB.getInt32Ty();
SanCovFunction = checkSanitizerInterfaceFunction(
M.getOrInsertFunction(SanCovName, VoidTy, Int32PtrTy, nullptr));
@ -296,7 +300,7 @@ bool SanitizerCoverageModule::runOnModule(Module &M) {
SanCovTracePC = checkSanitizerInterfaceFunction(
M.getOrInsertFunction(SanCovTracePCName, VoidTy, nullptr));
SanCovTracePCGuard = checkSanitizerInterfaceFunction(M.getOrInsertFunction(
SanCovTracePCGuardName, VoidTy, IntptrPtrTy, nullptr));
SanCovTracePCGuardName, VoidTy, Int32PtrTy, nullptr));
SanCovTraceEnter = checkSanitizerInterfaceFunction(
M.getOrInsertFunction(SanCovTraceEnterName, VoidTy, Int32PtrTy, nullptr));
SanCovTraceBB = checkSanitizerInterfaceFunction(
@ -307,9 +311,10 @@ bool SanitizerCoverageModule::runOnModule(Module &M) {
Type *Int32Ty = IRB.getInt32Ty();
Type *Int8Ty = IRB.getInt8Ty();
GuardArray =
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage,
nullptr, "__sancov_gen_cov_tmp");
if (!Options.TracePCGuard)
GuardArray =
new GlobalVariable(M, Int32Ty, false, GlobalValue::ExternalLinkage,
nullptr, "__sancov_gen_cov_tmp");
if (Options.Use8bitCounters)
EightBitCounterArray =
new GlobalVariable(M, Int8Ty, false, GlobalVariable::ExternalLinkage,
@ -320,17 +325,20 @@ bool SanitizerCoverageModule::runOnModule(Module &M) {
auto N = NumberOfInstrumentedBlocks();
// Now we know how many elements we need. Create an array of guards
// with one extra element at the beginning for the size.
Type *Int32ArrayNTy = ArrayType::get(Int32Ty, N + 1);
GlobalVariable *RealGuardArray = new GlobalVariable(
M, Int32ArrayNTy, false, GlobalValue::PrivateLinkage,
Constant::getNullValue(Int32ArrayNTy), "__sancov_gen_cov");
GlobalVariable *RealGuardArray = nullptr;
if (!Options.TracePCGuard) {
// Now we know how many elements we need. Create an array of guards
// with one extra element at the beginning for the size.
Type *Int32ArrayNTy = ArrayType::get(Int32Ty, N + 1);
RealGuardArray = new GlobalVariable(
M, Int32ArrayNTy, false, GlobalValue::PrivateLinkage,
Constant::getNullValue(Int32ArrayNTy), "__sancov_gen_cov");
// Replace the dummy array with the real one.
GuardArray->replaceAllUsesWith(
IRB.CreatePointerCast(RealGuardArray, Int32PtrTy));
GuardArray->eraseFromParent();
// Replace the dummy array with the real one.
GuardArray->replaceAllUsesWith(
IRB.CreatePointerCast(RealGuardArray, Int32PtrTy));
GuardArray->eraseFromParent();
}
GlobalVariable *RealEightBitCounterArray;
if (Options.Use8bitCounters) {
@ -359,16 +367,16 @@ bool SanitizerCoverageModule::runOnModule(Module &M) {
GlobalVariable *Bounds[2];
const char *Prefix[2] = {"__start_", "__stop_"};
for (int i = 0; i < 2; i++) {
Bounds[i] = new GlobalVariable(M, IntptrPtrTy, false,
Bounds[i] = new GlobalVariable(M, Int32PtrTy, false,
GlobalVariable::ExternalLinkage, nullptr,
Prefix[i] + SectionName);
Bounds[i]->setVisibility(GlobalValue::HiddenVisibility);
}
std::tie(CtorFunc, std::ignore) = createSanitizerCtorAndInitFunctions(
M, SanCovModuleCtorName, SanCovTracePCGuardInitName,
{IntptrPtrTy, IntptrPtrTy},
{IRB.CreatePointerCast(Bounds[0], IntptrPtrTy),
IRB.CreatePointerCast(Bounds[1], IntptrPtrTy)});
{Int32PtrTy, Int32PtrTy},
{IRB.CreatePointerCast(Bounds[0], Int32PtrTy),
IRB.CreatePointerCast(Bounds[1], Int32PtrTy)});
appendToGlobalCtors(M, CtorFunc, SanCtorAndDtorPriority);
}
@ -419,6 +427,14 @@ static bool isFullPostDominator(const BasicBlock *BB,
static bool shouldInstrumentBlock(const Function& F, const BasicBlock *BB, const DominatorTree *DT,
const PostDominatorTree *PDT) {
// Don't insert coverage for unreachable blocks: we will never call
// __sanitizer_cov() for them, so counting them in
// NumberOfInstrumentedBlocks() might complicate calculation of code coverage
// percentage. Also, unreachable instructions frequently have no debug
// locations.
if (isa<UnreachableInst>(BB->getTerminator()))
return false;
if (!ClPruneBlocks || &F.getEntryBlock() == BB)
return true;
@ -486,19 +502,34 @@ bool SanitizerCoverageModule::runOnFunction(Function &F) {
InjectTraceForGep(F, GepTraceTargets);
return true;
}
void SanitizerCoverageModule::CreateFunctionGuardArray(size_t NumGuards, Function &F) {
if (!Options.TracePCGuard) return;
HasSancovGuardsSection = true;
ArrayType *ArrayOfInt32Ty = ArrayType::get(Int32Ty, NumGuards);
FunctionGuardArray = new GlobalVariable(
*CurModule, ArrayOfInt32Ty, false, GlobalVariable::LinkOnceODRLinkage,
Constant::getNullValue(ArrayOfInt32Ty), "__sancov_guard." + F.getName());
if (auto Comdat = F.getComdat())
FunctionGuardArray->setComdat(Comdat);
FunctionGuardArray->setSection(SanCovTracePCGuardSection);
FunctionGuardArray->setVisibility(GlobalValue::HiddenVisibility);
}
bool SanitizerCoverageModule::InjectCoverage(Function &F,
ArrayRef<BasicBlock *> AllBlocks) {
if (AllBlocks.empty()) return false;
switch (Options.CoverageType) {
case SanitizerCoverageOptions::SCK_None:
return false;
case SanitizerCoverageOptions::SCK_Function:
InjectCoverageAtBlock(F, F.getEntryBlock(), false);
CreateFunctionGuardArray(1, F);
InjectCoverageAtBlock(F, F.getEntryBlock(), 0, false);
return true;
default: {
bool UseCalls = ClCoverageBlockThreshold < AllBlocks.size();
for (auto BB : AllBlocks)
InjectCoverageAtBlock(F, *BB, UseCalls);
CreateFunctionGuardArray(AllBlocks.size(), F);
for (size_t i = 0, N = AllBlocks.size(); i < N; i++)
InjectCoverageAtBlock(F, *AllBlocks[i], i, UseCalls);
return true;
}
}
@ -635,16 +666,8 @@ void SanitizerCoverageModule::SetNoSanitizeMetadata(Instruction *I) {
}
void SanitizerCoverageModule::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
bool UseCalls) {
// Don't insert coverage for unreachable blocks: we will never call
// __sanitizer_cov() for them, so counting them in
// NumberOfInstrumentedBlocks() might complicate calculation of code coverage
// percentage. Also, unreachable instructions frequently have no debug
// locations.
if (isa<UnreachableInst>(BB.getTerminator()))
return;
size_t Idx, bool UseCalls) {
BasicBlock::iterator IP = BB.getFirstInsertionPt();
bool IsEntryBB = &BB == &F.getEntryBlock();
DebugLoc EntryLoc;
if (IsEntryBB) {
@ -660,24 +683,22 @@ void SanitizerCoverageModule::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
IRBuilder<> IRB(&*IP);
IRB.SetCurrentDebugLocation(EntryLoc);
Value *GuardP = IRB.CreateAdd(
IRB.CreatePointerCast(GuardArray, IntptrTy),
ConstantInt::get(IntptrTy, (1 + NumberOfInstrumentedBlocks()) * 4));
Type *Int32PtrTy = PointerType::getUnqual(IRB.getInt32Ty());
GuardP = IRB.CreateIntToPtr(GuardP, Int32PtrTy);
if (Options.TracePC) {
IRB.CreateCall(SanCovTracePC); // gets the PC using GET_CALLER_PC.
IRB.CreateCall(EmptyAsm, {}); // Avoids callback merge.
} else if (Options.TracePCGuard) {
auto GuardVar = new GlobalVariable(
*F.getParent(), Int64Ty, false, GlobalVariable::LinkOnceODRLinkage,
Constant::getNullValue(Int64Ty), "__sancov_guard." + F.getName());
if (auto Comdat = F.getComdat())
GuardVar->setComdat(Comdat);
//auto GuardVar = new GlobalVariable(
// *F.getParent(), Int64Ty, false, GlobalVariable::LinkOnceODRLinkage,
// Constant::getNullValue(Int64Ty), "__sancov_guard." + F.getName());
// if (auto Comdat = F.getComdat())
// GuardVar->setComdat(Comdat);
// TODO: add debug into to GuardVar.
GuardVar->setSection(SanCovTracePCGuardSection);
HasSancovGuardsSection = true;
auto GuardPtr = IRB.CreatePointerCast(GuardVar, IntptrPtrTy);
// GuardVar->setSection(SanCovTracePCGuardSection);
// auto GuardPtr = IRB.CreatePointerCast(GuardVar, IntptrPtrTy);
auto GuardPtr = IRB.CreateIntToPtr(
IRB.CreateAdd(IRB.CreatePointerCast(FunctionGuardArray, IntptrTy),
ConstantInt::get(IntptrTy, Idx * 4)),
Int32PtrTy);
if (!UseCalls) {
auto GuardLoad = IRB.CreateLoad(GuardPtr);
GuardLoad->setAtomic(AtomicOrdering::Monotonic);
@ -692,24 +713,30 @@ void SanitizerCoverageModule::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
}
IRB.CreateCall(SanCovTracePCGuard, GuardPtr);
IRB.CreateCall(EmptyAsm, {}); // Avoids callback merge.
} else if (Options.TraceBB) {
IRB.CreateCall(IsEntryBB ? SanCovTraceEnter : SanCovTraceBB, GuardP);
} else if (UseCalls) {
IRB.CreateCall(SanCovWithCheckFunction, GuardP);
} else {
LoadInst *Load = IRB.CreateLoad(GuardP);
Load->setAtomic(AtomicOrdering::Monotonic);
Load->setAlignment(4);
SetNoSanitizeMetadata(Load);
Value *Cmp =
IRB.CreateICmpSGE(Constant::getNullValue(Load->getType()), Load);
Instruction *Ins = SplitBlockAndInsertIfThen(
Cmp, &*IP, false, MDBuilder(*C).createBranchWeights(1, 100000));
IRB.SetInsertPoint(Ins);
IRB.SetCurrentDebugLocation(EntryLoc);
// __sanitizer_cov gets the PC of the instruction using GET_CALLER_PC.
IRB.CreateCall(SanCovFunction, GuardP);
IRB.CreateCall(EmptyAsm, {}); // Avoids callback merge.
Value *GuardP = IRB.CreateAdd(
IRB.CreatePointerCast(GuardArray, IntptrTy),
ConstantInt::get(IntptrTy, (1 + NumberOfInstrumentedBlocks()) * 4));
GuardP = IRB.CreateIntToPtr(GuardP, Int32PtrTy);
if (Options.TraceBB) {
IRB.CreateCall(IsEntryBB ? SanCovTraceEnter : SanCovTraceBB, GuardP);
} else if (UseCalls) {
IRB.CreateCall(SanCovWithCheckFunction, GuardP);
} else {
LoadInst *Load = IRB.CreateLoad(GuardP);
Load->setAtomic(AtomicOrdering::Monotonic);
Load->setAlignment(4);
SetNoSanitizeMetadata(Load);
Value *Cmp =
IRB.CreateICmpSGE(Constant::getNullValue(Load->getType()), Load);
Instruction *Ins = SplitBlockAndInsertIfThen(
Cmp, &*IP, false, MDBuilder(*C).createBranchWeights(1, 100000));
IRB.SetInsertPoint(Ins);
IRB.SetCurrentDebugLocation(EntryLoc);
// __sanitizer_cov gets the PC of the instruction using GET_CALLER_PC.
IRB.CreateCall(SanCovFunction, GuardP);
IRB.CreateCall(EmptyAsm, {}); // Avoids callback merge.
}
}
if (Options.Use8bitCounters) {

View File

@ -9,5 +9,4 @@ entry:
ret void
}
; CHECK: @__sancov_guard.Foo = linkonce_odr global i64 0, section "__sancov_guards", comdat($Foo)
; CHECK: @__sancov_guard.Foo = linkonce_odr hidden global [1 x i32] zeroinitializer, section "__sancov_guards", comdat($Foo)

View File

@ -46,4 +46,4 @@ entry:
; CHECK_PC_GUARD: call void @__sanitizer_cov_trace_pc_guard
; CHECK_PC_GUARD-NOT: call void @__sanitizer_cov_trace_pc
; CHECK_PC_GUARD: ret void
; CHECK_PC_GUARD: call void @__sanitizer_cov_trace_pc_guard_init(i64* bitcast (i64** @__start___sancov_guards to i64*), i64* bitcast (i64** @__stop___sancov_guards to i64*))
; CHECK_PC_GUARD: call void @__sanitizer_cov_trace_pc_guard_init(i32* bitcast (i32** @__start___sancov_guards to i32*), i32* bitcast (i32** @__stop___sancov_guards to i32*))