forked from OSchip/llvm-project
[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
6178cfaf7b
commit
5c7fc76983
|
@ -307,6 +307,9 @@ def fsanitize_coverage_trace_pc_guard
|
|||
def fsanitize_coverage_no_prune
|
||||
: Flag<["-"], "fsanitize-coverage-no-prune">,
|
||||
HelpText<"Disable coverage pruning (i.e. instrument all blocks/edges)">;
|
||||
def fsanitize_coverage_stack_depth
|
||||
: Flag<["-"], "fsanitize-coverage-stack-depth">,
|
||||
HelpText<"Enable max stack depth tracing">;
|
||||
def fprofile_instrument_EQ : Joined<["-"], "fprofile-instrument=">,
|
||||
HelpText<"Enable PGO instrumentation. The accepted value is clang, llvm, "
|
||||
"or none">, Values<"none,clang,llvm">;
|
||||
|
|
|
@ -169,6 +169,7 @@ CODEGENOPT(SanitizeCoverageTracePCGuard, 1, 0) ///< Enable PC tracing with guard
|
|||
CODEGENOPT(SanitizeCoverageInline8bitCounters, 1, 0) ///< Use inline 8bit counters.
|
||||
CODEGENOPT(SanitizeCoveragePCTable, 1, 0) ///< Create a PC Table.
|
||||
CODEGENOPT(SanitizeCoverageNoPrune, 1, 0) ///< Disable coverage pruning.
|
||||
CODEGENOPT(SanitizeCoverageStackDepth, 1, 0) ///< Enable max stack depth tracing
|
||||
CODEGENOPT(SanitizeStats , 1, 0) ///< Collect statistics for sanitizers.
|
||||
CODEGENOPT(SimplifyLibCalls , 1, 1) ///< Set when -fbuiltin is enabled.
|
||||
CODEGENOPT(SoftFloat , 1, 0) ///< -soft-float.
|
||||
|
|
|
@ -190,6 +190,7 @@ static void addSanitizerCoveragePass(const PassManagerBuilder &Builder,
|
|||
Opts.NoPrune = CGOpts.SanitizeCoverageNoPrune;
|
||||
Opts.Inline8bitCounters = CGOpts.SanitizeCoverageInline8bitCounters;
|
||||
Opts.PCTable = CGOpts.SanitizeCoveragePCTable;
|
||||
Opts.StackDepth = CGOpts.SanitizeCoverageStackDepth;
|
||||
PM.add(createSanitizerCoverageModulePass(Opts));
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ enum CoverageFeature {
|
|||
CoverageNoPrune = 1 << 11,
|
||||
CoverageInline8bitCounters = 1 << 12,
|
||||
CoveragePCTable = 1 << 13,
|
||||
CoverageStackDepth = 1 << 14,
|
||||
};
|
||||
|
||||
/// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
|
||||
|
@ -556,11 +557,15 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
|
|||
}
|
||||
|
||||
// trace-pc w/o func/bb/edge implies edge.
|
||||
if ((CoverageFeatures &
|
||||
(CoverageTracePC | CoverageTracePCGuard | CoverageInline8bitCounters)) &&
|
||||
!(CoverageFeatures & InsertionPointTypes))
|
||||
if (!(CoverageFeatures & InsertionPointTypes)) {
|
||||
if (CoverageFeatures &
|
||||
(CoverageTracePC | CoverageTracePCGuard | CoverageInline8bitCounters))
|
||||
CoverageFeatures |= CoverageEdge;
|
||||
|
||||
if (CoverageFeatures & CoverageStackDepth)
|
||||
CoverageFeatures |= CoverageFunc;
|
||||
}
|
||||
|
||||
if (AllAddedKinds & Address) {
|
||||
AsanSharedRuntime =
|
||||
Args.hasArg(options::OPT_shared_libasan) ||
|
||||
|
@ -672,7 +677,8 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
|
|||
std::make_pair(CoverageTracePCGuard, "-fsanitize-coverage-trace-pc-guard"),
|
||||
std::make_pair(CoverageInline8bitCounters, "-fsanitize-coverage-inline-8bit-counters"),
|
||||
std::make_pair(CoveragePCTable, "-fsanitize-coverage-pc-table"),
|
||||
std::make_pair(CoverageNoPrune, "-fsanitize-coverage-no-prune")};
|
||||
std::make_pair(CoverageNoPrune, "-fsanitize-coverage-no-prune"),
|
||||
std::make_pair(CoverageStackDepth, "-fsanitize-coverage-stack-depth")};
|
||||
for (auto F : CoverageFlags) {
|
||||
if (CoverageFeatures & F.first)
|
||||
CmdArgs.push_back(F.second);
|
||||
|
@ -835,6 +841,7 @@ int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A) {
|
|||
.Case("no-prune", CoverageNoPrune)
|
||||
.Case("inline-8bit-counters", CoverageInline8bitCounters)
|
||||
.Case("pc-table", CoveragePCTable)
|
||||
.Case("stack-depth", CoverageStackDepth)
|
||||
.Default(0);
|
||||
if (F == 0)
|
||||
D.Diag(clang::diag::err_drv_unsupported_option_argument)
|
||||
|
|
|
@ -795,6 +795,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,
|
|||
Opts.SanitizeCoverageInline8bitCounters =
|
||||
Args.hasArg(OPT_fsanitize_coverage_inline_8bit_counters);
|
||||
Opts.SanitizeCoveragePCTable = Args.hasArg(OPT_fsanitize_coverage_pc_table);
|
||||
Opts.SanitizeCoverageStackDepth =
|
||||
Args.hasArg(OPT_fsanitize_coverage_stack_depth);
|
||||
Opts.SanitizeMemoryTrackOrigins =
|
||||
getLastArgIntValue(Args, OPT_fsanitize_memory_track_origins_EQ, 0, Diags);
|
||||
Opts.SanitizeMemoryUseAfterDtor =
|
||||
|
|
|
@ -72,6 +72,17 @@
|
|||
// CHECK-TRACE_PC_GUARD_FUNC: -fsanitize-coverage-type=1
|
||||
// CHECK-TRACE_PC_GUARD_FUNC: -fsanitize-coverage-trace-pc-guard
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize-coverage=stack-depth %s \
|
||||
// RUN: -### 2>&1 | FileCheck %s --check-prefix=CHECK-STACK-DEPTH
|
||||
// RUN: %clang -target x86_64-linux-gnu \
|
||||
// RUN: -fsanitize-coverage=trace-pc-guard,stack-depth %s -### 2>&1 | \
|
||||
// RUN: FileCheck %s --check-prefix=CHECK-STACK-DEPTH-PC-GUARD
|
||||
// CHECK-STACK-DEPTH: -fsanitize-coverage-type=1
|
||||
// CHECK-STACK-DEPTH: -fsanitize-coverage-stack-depth
|
||||
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-type=3
|
||||
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-trace-pc-guard
|
||||
// CHECK-STACK-DEPTH-PC-GUARD: -fsanitize-coverage-stack-depth
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address -fsanitize-coverage=trace-cmp,indirect-calls %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-TYPE-NECESSARY
|
||||
// CHECK-NO-TYPE-NECESSARY-NOT: error:
|
||||
// CHECK-NO-TYPE-NECESSARY: -fsanitize-coverage-indirect-calls
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Tests -fsanitize-coverage=stack-depth
|
||||
//
|
||||
// XFAIL: tsan
|
||||
//
|
||||
// RUN: %clangxx -O0 -std=c++11 -fsanitize-coverage=stack-depth %s -o %t
|
||||
// RUN: %run %t 2>&1 | FileCheck %s --implicit-check-not Assertion{{.*}}failed
|
||||
// RUN: %clangxx -O0 -std=c++11 -fsanitize-coverage=trace-pc-guard,stack-depth \
|
||||
// RUN: %s -o %t
|
||||
// RUN: %run %t 2>&1 | FileCheck %s --implicit-check-not Assertion{{.*}}failed
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cassert>
|
||||
|
||||
thread_local uintptr_t __sancov_lowest_stack;
|
||||
uintptr_t last_stack;
|
||||
|
||||
void foo(int recurse) {
|
||||
assert(__sancov_lowest_stack < last_stack);
|
||||
last_stack = __sancov_lowest_stack;
|
||||
if (recurse <= 0) return;
|
||||
foo(recurse - 1);
|
||||
}
|
||||
|
||||
int main() {
|
||||
last_stack = __sancov_lowest_stack;
|
||||
foo(100);
|
||||
printf("Success!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// CHECK: Success!
|
|
@ -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
|
||||
|
|
|
@ -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