diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h index 41f209ebae9..fe458e7be06 100644 --- a/include/llvm/Transforms/Instrumentation.h +++ b/include/llvm/Transforms/Instrumentation.h @@ -185,6 +185,7 @@ struct SanitizerCoverageOptions { bool Inline8bitCounters = false; bool PCTable = false; bool NoPrune = false; + bool StackDepth = false; SanitizerCoverageOptions() = default; }; diff --git a/lib/Fuzzer/FuzzerTracePC.cpp b/lib/Fuzzer/FuzzerTracePC.cpp index a54a8c1e99f..ebd33d3ec88 100644 --- a/lib/Fuzzer/FuzzerTracePC.cpp +++ b/lib/Fuzzer/FuzzerTracePC.cpp @@ -31,6 +31,9 @@ uint8_t __sancov_trace_pc_guard_8bit_counters[fuzzer::TracePC::kNumPCs]; ATTRIBUTE_INTERFACE uintptr_t __sancov_trace_pc_pcs[fuzzer::TracePC::kNumPCs]; +// Used by -fsanitize-coverage=stack-depth to track stack depth +ATTRIBUTE_INTERFACE thread_local uintptr_t __sancov_lowest_stack; + namespace fuzzer { TracePC TPC; @@ -340,6 +343,14 @@ void TracePC::ClearInlineCounters() { } } +void TracePC::RecordInitialStack() { + InitialStack = __sancov_lowest_stack; +} + +uintptr_t TracePC::GetMaxStackOffset() const { + return InitialStack - __sancov_lowest_stack; // Stack grows down +} + } // namespace fuzzer extern "C" { @@ -350,8 +361,6 @@ void __sanitizer_cov_trace_pc_guard(uint32_t *Guard) { uint32_t Idx = *Guard; __sancov_trace_pc_pcs[Idx] = PC; __sancov_trace_pc_guard_8bit_counters[Idx]++; - // Uncomment the following line to get stack-depth profiling. - // fuzzer::TPC.RecordCurrentStack(); } // Best-effort support for -fsanitize-coverage=trace-pc, which is available diff --git a/lib/Fuzzer/FuzzerTracePC.h b/lib/Fuzzer/FuzzerTracePC.h index 40827b32828..56f1820f79e 100644 --- a/lib/Fuzzer/FuzzerTracePC.h +++ b/lib/Fuzzer/FuzzerTracePC.h @@ -120,19 +120,8 @@ class TracePC { return PCs()[Idx]; } - void RecordCurrentStack() { - uintptr_t Stack = GetCurrentStack(); - if (Stack < LowestStack) - LowestStack = Stack; - } - void RecordInitialStack() { - InitialStack = GetCurrentStack(); - LowestStack = InitialStack; - } - uintptr_t GetCurrentStack() const { - return reinterpret_cast(__builtin_frame_address(0)); - } - uintptr_t GetMaxStackOffset() const { return InitialStack - LowestStack; } + void RecordInitialStack(); + uintptr_t GetMaxStackOffset() const; template void ForEachObservedPC(CallBack CB) { @@ -167,7 +156,7 @@ private: std::set ObservedPCs; ValueBitMap ValueProfileMap; - uintptr_t InitialStack, LowestStack; // Assume stack grows down. + uintptr_t InitialStack; }; template diff --git a/lib/Transforms/Instrumentation/SanitizerCoverage.cpp b/lib/Transforms/Instrumentation/SanitizerCoverage.cpp index de4b8da78fe..8a12c0fb387 100644 --- a/lib/Transforms/Instrumentation/SanitizerCoverage.cpp +++ b/lib/Transforms/Instrumentation/SanitizerCoverage.cpp @@ -17,12 +17,15 @@ #include "llvm/Analysis/PostDominators.h" #include "llvm/IR/CFG.h" #include "llvm/IR/CallSite.h" +#include "llvm/IR/Constant.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/Dominators.h" #include "llvm/IR/Function.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/InlineAsm.h" +#include "llvm/IR/Intrinsics.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/Module.h" @@ -73,6 +76,10 @@ static const char *const SanCovGuardsSectionName = "sancov_guards"; static const char *const SanCovCountersSectionName = "sancov_cntrs"; static const char *const SanCovPCsSectionName = "sancov_pcs"; +static const char *const SanCovLowestStackName = "__sancov_lowest_stack"; +static const char *const SanCovLowestStackTLSWrapperName = + "_ZTW21__sancov_lowest_stack"; + static cl::opt ClCoverageLevel( "sanitizer-coverage-level", cl::desc("Sanitizer Coverage. 0: none, 1: entry block, 2: all blocks, " @@ -119,6 +126,10 @@ static cl::opt cl::desc("Reduce the number of instrumented blocks"), cl::Hidden, cl::init(true)); +static cl::opt ClStackDepth("sanitizer-coverage-stack-depth", + cl::desc("max stack depth tracing"), + cl::Hidden, cl::init(false)); + namespace { SanitizerCoverageOptions getOptions(int LegacyCoverageLevel) { @@ -156,9 +167,11 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) { Options.TracePCGuard |= ClTracePCGuard; Options.Inline8bitCounters |= ClInline8bitCounters; Options.PCTable |= ClCreatePCTable; - if (!Options.TracePCGuard && !Options.TracePC && !Options.Inline8bitCounters) - Options.TracePCGuard = true; // TracePCGuard is default. Options.NoPrune |= !ClPruneBlocks; + Options.StackDepth |= ClStackDepth; + if (!Options.TracePCGuard && !Options.TracePC && + !Options.Inline8bitCounters && !Options.StackDepth) + Options.TracePCGuard = true; // TracePCGuard is default. return Options; } @@ -216,6 +229,8 @@ private: Function *SanCovTraceDivFunction[2]; Function *SanCovTraceGepFunction; Function *SanCovTraceSwitchFunction; + Function *SanCovLowestStackTLSWrapper; + GlobalVariable *SanCovLowestStack; InlineAsm *EmptyAsm; Type *IntptrTy, *IntptrPtrTy, *Int64Ty, *Int64PtrTy, *Int32Ty, *Int32PtrTy, *Int16Ty, *Int8Ty, *Int8PtrTy; @@ -333,6 +348,24 @@ bool SanitizerCoverageModule::runOnModule(Module &M) { SanCovTraceSwitchFunction = checkSanitizerInterfaceFunction(M.getOrInsertFunction( SanCovTraceSwitchName, VoidTy, Int64Ty, Int64PtrTy)); + + Constant *SanCovLowestStackConstant = + M.getOrInsertGlobal(SanCovLowestStackName, IntptrTy); + SanCovLowestStackTLSWrapper = + checkSanitizerInterfaceFunction(M.getOrInsertFunction( + SanCovLowestStackTLSWrapperName, IntptrTy->getPointerTo())); + if (Options.StackDepth) { + assert(isa(SanCovLowestStackConstant)); + SanCovLowestStack = cast(SanCovLowestStackConstant); + if (!SanCovLowestStack->isDeclaration()) { + // Check that the user has correctly defined: + // thread_local uintptr_t __sancov_lowest_stack + // and initialize it. + assert(SanCovLowestStack->isThreadLocal()); + SanCovLowestStack->setInitializer(Constant::getAllOnesValue(IntptrTy)); + } + } + // Make sure smaller parameters are zero-extended to i64 as required by the // x86_64 ABI. if (TargetTriple.getArch() == Triple::x86_64) { @@ -451,6 +484,9 @@ bool SanitizerCoverageModule::runOnFunction(Function &F) { if (F.getName() == "__local_stdio_printf_options" || F.getName() == "__local_stdio_scanf_options") return false; + // Avoid infinite recursion by not instrumenting stack depth TLS wrapper + if (F.getName() == SanCovLowestStackTLSWrapperName) + return false; // Don't instrument functions using SEH for now. Splitting basic blocks like // we do for coverage breaks WinEHPrepare. // FIXME: Remove this when SEH no longer uses landingpad pattern matching. @@ -728,6 +764,20 @@ void SanitizerCoverageModule::InjectCoverageAtBlock(Function &F, BasicBlock &BB, SetNoSanitizeMetadata(Load); SetNoSanitizeMetadata(Store); } + if (Options.StackDepth && IsEntryBB) { + // Check stack depth. If it's the deepest so far, record it. + Function *GetFrameAddr = + Intrinsic::getDeclaration(F.getParent(), Intrinsic::frameaddress); + auto FrameAddrPtr = + IRB.CreateCall(GetFrameAddr, {Constant::getNullValue(Int32Ty)}); + auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy); + auto LowestStackPtr = IRB.CreateCall(SanCovLowestStackTLSWrapper); + auto LowestStack = IRB.CreateLoad(LowestStackPtr); + auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack); + auto ThenTerm = SplitBlockAndInsertIfThen(IsStackLower, &*IP, false); + IRBuilder<> ThenIRB(ThenTerm); + ThenIRB.CreateStore(FrameAddrInt, LowestStackPtr); + } } std::string diff --git a/test/Instrumentation/SanitizerCoverage/stack-depth.ll b/test/Instrumentation/SanitizerCoverage/stack-depth.ll new file mode 100644 index 00000000000..015f5676bc8 --- /dev/null +++ b/test/Instrumentation/SanitizerCoverage/stack-depth.ll @@ -0,0 +1,50 @@ +; This check verifies that stack depth instrumentation works correctly. +; RUN: opt < %s -sancov -sanitizer-coverage-level=1 \ +; RUN: -sanitizer-coverage-stack-depth -S | FileCheck %s --enable-var-scope +; RUN: opt < %s -sancov -sanitizer-coverage-level=3 \ +; RUN: -sanitizer-coverage-stack-depth -sanitizer-coverage-trace-pc-guard \ +; RUN: -S | FileCheck %s --enable-var-scope + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; CHECK: @__sancov_lowest_stack = thread_local global i64 -1 +@__sancov_lowest_stack = thread_local global i64 0, align 8 + +define i32 @foo() { +entry: +; CHECK-LABEL: define i32 @foo +; CHECK: [[framePtr:%[^ \t]+]] = call i8* @llvm.frameaddress(i32 0) +; CHECK: [[frameInt:%[^ \t]+]] = ptrtoint i8* [[framePtr]] to [[$intType:i[0-9]+]] +; CHECK: [[lowestPtr:%[^ \t]+]] = call [[$intType]]* @_ZTW21__sancov_lowest_stack +; CHECK: [[lowestInt:%[^ \t]+]] = load [[$intType]], [[$intType]]* [[lowestPtr]] +; CHECK: [[cmp:%[^ \t]+]] = icmp ult [[$intType]] [[frameInt]], [[lowestInt]] +; CHECK: br i1 [[cmp]], label %[[ifLabel:[^ \t]+]], label +; CHECK: