Skip to content

Commit ed85b47

Browse files
committed
[sancov] Introduce optional callback for stack-depth tracking
Normally -fsanitize-coverage=stack-depth inserts inline arithmetic to update thread_local __sancov_lowest_stack. To support stack depth tracking in the Linux kernel, which does not implement traditional thread_local storage, provide the option to call a function instead. This matches the existing "stackleak" implementation that is supported in Linux via a GCC plugin. To make this coverage more performant, a minimum estimated stack depth can be chosen to enable the callback mode, skipping instrumentation of functions with smaller stacks. With -fsanitize-coverage-stack-depth-callback-min set greater than 0, the __sanitize_cov_stack_depth() callback will be injected when the estimated stack depth is greater than or equal to the given minimum.
1 parent f46ff4c commit ed85b47

File tree

7 files changed

+73
-15
lines changed

7 files changed

+73
-15
lines changed

clang/include/clang/Basic/CodeGenOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ CODEGENOPT(SanitizeCoveragePCTable, 1, 0) ///< Create a PC Table.
305305
CODEGENOPT(SanitizeCoverageControlFlow, 1, 0) ///< Collect control flow
306306
CODEGENOPT(SanitizeCoverageNoPrune, 1, 0) ///< Disable coverage pruning.
307307
CODEGENOPT(SanitizeCoverageStackDepth, 1, 0) ///< Enable max stack depth tracing
308+
VALUE_CODEGENOPT(SanitizeCoverageStackDepthCallbackMin , 32, 0) ///< Enable stack depth tracing callbacks.
308309
CODEGENOPT(SanitizeCoverageTraceLoads, 1, 0) ///< Enable tracing of loads.
309310
CODEGENOPT(SanitizeCoverageTraceStores, 1, 0) ///< Enable tracing of stores.
310311
CODEGENOPT(SanitizeBinaryMetadataCovered, 1, 0) ///< Emit PCs for covered functions.

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,6 +2361,11 @@ def fsanitize_coverage_ignorelist : Joined<["-"], "fsanitize-coverage-ignorelist
23612361
HelpText<"Disable sanitizer coverage instrumentation for modules and functions "
23622362
"that match the provided special case list, even the allowed ones">,
23632363
MarshallingInfoStringVector<CodeGenOpts<"SanitizeCoverageIgnorelistFiles">>;
2364+
def fsanitize_coverage_stack_depth_callback_min_EQ :
2365+
Joined<["-"], "fsanitize-coverage-stack-depth-callback-min=">,
2366+
Group<f_clang_Group>, MetaVarName<"<M>">,
2367+
HelpText<"Use callback for max stack depth tracing with minimum stack depth M">,
2368+
MarshallingInfoInt<CodeGenOpts<"SanitizeCoverageStackDepthCallbackMin">>;
23642369
def fexperimental_sanitize_metadata_EQ : CommaJoined<["-"], "fexperimental-sanitize-metadata=">,
23652370
Group<f_Group>,
23662371
HelpText<"Specify the type of metadata to emit for binary analysis sanitizers">;

clang/include/clang/Driver/SanitizerArgs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class SanitizerArgs {
3434
std::vector<std::string> CoverageIgnorelistFiles;
3535
std::vector<std::string> BinaryMetadataIgnorelistFiles;
3636
int CoverageFeatures = 0;
37+
int StackDepthCallbackMin = 0;
3738
int BinaryMetadataFeatures = 0;
3839
int OverflowPatternExclusions = 0;
3940
int MsanTrackOrigins = 0;

clang/lib/CodeGen/BackendUtil.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ getSancovOptsFromCGOpts(const CodeGenOptions &CGOpts) {
255255
Opts.InlineBoolFlag = CGOpts.SanitizeCoverageInlineBoolFlag;
256256
Opts.PCTable = CGOpts.SanitizeCoveragePCTable;
257257
Opts.StackDepth = CGOpts.SanitizeCoverageStackDepth;
258+
Opts.StackDepthCallbackMin = CGOpts.SanitizeCoverageStackDepthCallbackMin;
258259
Opts.TraceLoads = CGOpts.SanitizeCoverageTraceLoads;
259260
Opts.TraceStores = CGOpts.SanitizeCoverageTraceStores;
260261
Opts.CollectControlFlow = CGOpts.SanitizeCoverageControlFlow;

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,6 +751,16 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
751751
options::OPT_fno_sanitize_ignorelist,
752752
clang::diag::err_drv_malformed_sanitizer_ignorelist, DiagnoseErrors);
753753

754+
// Verify that -fsanitize-coverage-stack-depth-callback-min is >= 0.
755+
if (Arg *A = Args.getLastArg(options::OPT_fsanitize_coverage_stack_depth_callback_min_EQ)) {
756+
StringRef S = A->getValue();
757+
if (S.getAsInteger(0, StackDepthCallbackMin) || StackDepthCallbackMin < 0) {
758+
if (DiagnoseErrors)
759+
D.Diag(clang::diag::err_drv_invalid_value)
760+
<< A->getAsString(Args) << S;
761+
}
762+
}
763+
754764
// Parse -f[no-]sanitize-memory-track-origins[=level] options.
755765
if (AllAddedKinds & SanitizerKind::Memory) {
756766
if (Arg *A =
@@ -1269,6 +1279,10 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
12691279
addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-coverage-ignorelist=",
12701280
CoverageIgnorelistFiles);
12711281

1282+
if (StackDepthCallbackMin)
1283+
CmdArgs.push_back(Args.MakeArgString("-fsanitize-coverage-stack-depth-callback-min=" +
1284+
Twine(StackDepthCallbackMin)));
1285+
12721286
if (!GPUSanitize) {
12731287
// Translate available BinaryMetadataFeatures to corresponding clang-cc1
12741288
// flags. Does not depend on any other sanitizers. Unsupported on GPUs.

llvm/include/llvm/Transforms/Utils/Instrumentation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ struct SanitizerCoverageOptions {
158158
bool PCTable = false;
159159
bool NoPrune = false;
160160
bool StackDepth = false;
161+
int StackDepthCallbackMin = 0;
161162
bool TraceLoads = false;
162163
bool TraceStores = false;
163164
bool CollectControlFlow = false;

llvm/lib/Transforms/Instrumentation/SanitizerCoverage.cpp

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "llvm/Support/CommandLine.h"
3434
#include "llvm/Support/SpecialCaseList.h"
3535
#include "llvm/Support/VirtualFileSystem.h"
36+
#include "llvm/Support/raw_ostream.h"
3637
#include "llvm/TargetParser/Triple.h"
3738
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
3839
#include "llvm/Transforms/Utils/ModuleUtils.h"
@@ -86,6 +87,7 @@ const char SanCovPCsSectionName[] = "sancov_pcs";
8687
const char SanCovCFsSectionName[] = "sancov_cfs";
8788
const char SanCovCallbackGateSectionName[] = "sancov_gate";
8889

90+
const char SanCovStackDepthCallbackName[] = "__sanitizer_cov_stack_depth";
8991
const char SanCovLowestStackName[] = "__sancov_lowest_stack";
9092
const char SanCovCallbackGateName[] = "__sancov_should_track";
9193

@@ -152,6 +154,12 @@ static cl::opt<bool> ClStackDepth("sanitizer-coverage-stack-depth",
152154
cl::desc("max stack depth tracing"),
153155
cl::Hidden);
154156

157+
static cl::opt<int> ClStackDepthCallbackMin(
158+
"sanitizer-coverage-stack-depth-callback-min",
159+
cl::desc("max stack depth tracing should use callback and only when "
160+
"stack depth more than specified"),
161+
cl::Hidden);
162+
155163
static cl::opt<bool>
156164
ClCollectCF("sanitizer-coverage-control-flow",
157165
cl::desc("collect control flow for each function"), cl::Hidden);
@@ -202,6 +210,8 @@ SanitizerCoverageOptions OverrideFromCL(SanitizerCoverageOptions Options) {
202210
Options.PCTable |= ClCreatePCTable;
203211
Options.NoPrune |= !ClPruneBlocks;
204212
Options.StackDepth |= ClStackDepth;
213+
Options.StackDepthCallbackMin = std::max(Options.StackDepthCallbackMin,
214+
ClStackDepthCallbackMin.getValue());
205215
Options.TraceLoads |= ClLoadTracing;
206216
Options.TraceStores |= ClStoreTracing;
207217
Options.GatedCallbacks |= ClGatedCallbacks;
@@ -271,6 +281,7 @@ class ModuleSanitizerCoverage {
271281
DomTreeCallback DTCallback;
272282
PostDomTreeCallback PDTCallback;
273283

284+
FunctionCallee SanCovStackDepthCallback;
274285
FunctionCallee SanCovTracePCIndir;
275286
FunctionCallee SanCovTracePC, SanCovTracePCGuard;
276287
std::array<FunctionCallee, 4> SanCovTraceCmpFunction;
@@ -514,6 +525,8 @@ bool ModuleSanitizerCoverage::instrumentModule() {
514525
SanCovTracePCGuard =
515526
M.getOrInsertFunction(SanCovTracePCGuardName, VoidTy, PtrTy);
516527

528+
SanCovStackDepthCallback = M.getOrInsertFunction(SanCovStackDepthCallbackName, VoidTy);
529+
517530
for (auto &F : M)
518531
instrumentFunction(F);
519532

@@ -1078,22 +1091,44 @@ void ModuleSanitizerCoverage::InjectCoverageAtBlock(Function &F, BasicBlock &BB,
10781091
Store->setNoSanitizeMetadata();
10791092
}
10801093
if (Options.StackDepth && IsEntryBB && !IsLeafFunc) {
1081-
// Check stack depth. If it's the deepest so far, record it.
10821094
Module *M = F.getParent();
1083-
auto FrameAddrPtr = IRB.CreateIntrinsic(
1084-
Intrinsic::frameaddress,
1085-
IRB.getPtrTy(M->getDataLayout().getAllocaAddrSpace()),
1086-
{Constant::getNullValue(Int32Ty)});
1087-
auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy);
1088-
auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack);
1089-
auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack);
1090-
auto ThenTerm = SplitBlockAndInsertIfThen(
1091-
IsStackLower, &*IP, false,
1092-
MDBuilder(IRB.getContext()).createUnlikelyBranchWeights());
1093-
IRBuilder<> ThenIRB(ThenTerm);
1094-
auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack);
1095-
LowestStack->setNoSanitizeMetadata();
1096-
Store->setNoSanitizeMetadata();
1095+
if (Options.StackDepthCallbackMin) {
1096+
// In callback mode, only add call when stack depth reaches minimum.
1097+
const DataLayout &DL = M->getDataLayout();
1098+
uint32_t EstimatedStackSize = 0;
1099+
1100+
// Make an estimate on the stack usage.
1101+
for (auto &I : F.getEntryBlock()) {
1102+
if (auto *AI = dyn_cast<AllocaInst>(&I)) {
1103+
if (AI->isStaticAlloca()) {
1104+
uint32_t TypeSize = DL.getTypeAllocSize(AI->getAllocatedType());
1105+
EstimatedStackSize += TypeSize;
1106+
} else {
1107+
// Over-estimate dynamic sizes.
1108+
EstimatedStackSize += 4096;
1109+
}
1110+
}
1111+
}
1112+
1113+
if (EstimatedStackSize >= Options.StackDepthCallbackMin)
1114+
IRB.CreateCall(SanCovStackDepthCallback)->setCannotMerge();
1115+
} else {
1116+
// Check stack depth. If it's the deepest so far, record it.
1117+
auto FrameAddrPtr = IRB.CreateIntrinsic(
1118+
Intrinsic::frameaddress,
1119+
IRB.getPtrTy(M->getDataLayout().getAllocaAddrSpace()),
1120+
{Constant::getNullValue(Int32Ty)});
1121+
auto FrameAddrInt = IRB.CreatePtrToInt(FrameAddrPtr, IntptrTy);
1122+
auto LowestStack = IRB.CreateLoad(IntptrTy, SanCovLowestStack);
1123+
auto IsStackLower = IRB.CreateICmpULT(FrameAddrInt, LowestStack);
1124+
auto ThenTerm = SplitBlockAndInsertIfThen(
1125+
IsStackLower, &*IP, false,
1126+
MDBuilder(IRB.getContext()).createUnlikelyBranchWeights());
1127+
IRBuilder<> ThenIRB(ThenTerm);
1128+
auto Store = ThenIRB.CreateStore(FrameAddrInt, SanCovLowestStack);
1129+
LowestStack->setNoSanitizeMetadata();
1130+
Store->setNoSanitizeMetadata();
1131+
}
10971132
}
10981133
}
10991134

0 commit comments

Comments
 (0)