mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 02:33:06 +01:00
[SanitizerCoverage] Add stack depth tracing instrumentation.
Summary: Augment SanitizerCoverage to insert maximum stack depth tracing for use by libFuzzer. The new instrumentation is enabled by the flag -fsanitize-coverage=stack-depth and is compatible with the existing trace-pc-guard coverage. The user must also declare the following global variable in their code: thread_local uintptr_t __sancov_lowest_stack https://bugs.llvm.org/show_bug.cgi?id=33857 Reviewers: vitalybuka, kcc Reviewed By: vitalybuka Subscribers: kubamracek, hiraditya, cfe-commits, llvm-commits Differential Revision: https://reviews.llvm.org/D36839 llvm-svn: 311186
This commit is contained in:
parent
0cbccbe294
commit
38756d86aa
@ -185,6 +185,7 @@ struct SanitizerCoverageOptions {
|
||||
bool Inline8bitCounters = false;
|
||||
bool PCTable = false;
|
||||
bool NoPrune = false;
|
||||
bool StackDepth = false;
|
||||
|
||||
SanitizerCoverageOptions() = default;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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<uintptr_t>(__builtin_frame_address(0));
|
||||
}
|
||||
uintptr_t GetMaxStackOffset() const { return InitialStack - LowestStack; }
|
||||
void RecordInitialStack();
|
||||
uintptr_t GetMaxStackOffset() const;
|
||||
|
||||
template<class CallBack>
|
||||
void ForEachObservedPC(CallBack CB) {
|
||||
@ -167,7 +156,7 @@ private:
|
||||
std::set<uintptr_t> ObservedPCs;
|
||||
|
||||
ValueBitMap ValueProfileMap;
|
||||
uintptr_t InitialStack, LowestStack; // Assume stack grows down.
|
||||
uintptr_t InitialStack;
|
||||
};
|
||||
|
||||
template <class Callback>
|
||||
|
@ -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<int> ClCoverageLevel(
|
||||
"sanitizer-coverage-level",
|
||||
cl::desc("Sanitizer Coverage. 0: none, 1: entry block, 2: all blocks, "
|
||||
@ -119,6 +126,10 @@ static cl::opt<bool>
|
||||
cl::desc("Reduce the number of instrumented blocks"),
|
||||
cl::Hidden, cl::init(true));
|
||||
|
||||
static cl::opt<bool> 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<GlobalVariable>(SanCovLowestStackConstant));
|
||||
SanCovLowestStack = cast<GlobalVariable>(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
|
||||
|
50
test/Instrumentation/SanitizerCoverage/stack-depth.ll
Normal file
50
test/Instrumentation/SanitizerCoverage/stack-depth.ll
Normal file
@ -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: <label>:[[ifLabel]]:
|
||||
; CHECK: store [[$intType]] [[frameInt]], [[$intType]]* [[lowestPtr]]
|
||||
; CHECK: ret i32 7
|
||||
|
||||
ret i32 7
|
||||
}
|
||||
|
||||
define i32 @bar() {
|
||||
entry:
|
||||
; CHECK-LABEL: define i32 @bar
|
||||
; CHECK: [[framePtr:%[^ \t]+]] = call i8* @llvm.frameaddress(i32 0)
|
||||
; CHECK: [[frameInt:%[^ \t]+]] = ptrtoint i8* [[framePtr]] to [[$intType]]
|
||||
; 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: <label>:[[ifLabel]]:
|
||||
; CHECK: store [[$intType]] [[frameInt]], [[$intType]]* [[lowestPtr]]
|
||||
; CHECK: %call = call i32 @foo()
|
||||
; CHECK: ret i32 %call
|
||||
|
||||
%call = call i32 @foo()
|
||||
ret i32 %call
|
||||
}
|
||||
|
||||
define weak_odr hidden i64* @_ZTW21__sancov_lowest_stack() {
|
||||
ret i64* @__sancov_lowest_stack
|
||||
}
|
Loading…
Reference in New Issue
Block a user