Skip to content

Commit 2c0da9a

Browse files
committed
[ubsan] Add -fsanitize-nonmerged-handlers (and -fno-sanitize-nonmerged-handlers)
'-mllvm -ubsan-unique-traps' (llvm#65972) applies to all UBSan checks. This patch introduces -fsanitize-nonmerged-handlers and -fno-sanitize-nonmerged-handlers, which allows selectively applying non-merged handlers to a subset of UBSan checks. N.B. we use "non-merged handlers" instead of "unique traps", since llvm#119302 has generalized it to work for non-trap mode as well (min-rt and regular rt). This patch does not remove the -ubsan-unique-traps flag; that will override -f(no-)sanitize-non-merged-handlers.
1 parent c48d45e commit 2c0da9a

File tree

7 files changed

+55
-12
lines changed

7 files changed

+55
-12
lines changed

clang/include/clang/Basic/CodeGenOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
380380
/// Set of sanitizer checks that trap rather than diagnose.
381381
SanitizerSet SanitizeTrap;
382382

383+
/// Set of sanitizer checks that have non-merged handlers (better
384+
/// debuggability at the expense of code size).
385+
SanitizerSet SanitizeNonMergedHandlers;
386+
383387
/// List of backend command-line options for -fembed-bitcode.
384388
std::vector<uint8_t> CmdArgs;
385389

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2548,6 +2548,10 @@ def fsanitize_trap_EQ : CommaJoined<["-"], "fsanitize-trap=">, Group<f_clang_Gro
25482548
def fno_sanitize_trap_EQ : CommaJoined<["-"], "fno-sanitize-trap=">, Group<f_clang_Group>,
25492549
Visibility<[ClangOption, CLOption]>,
25502550
HelpText<"Disable trapping for specified sanitizers">;
2551+
def fsanitize_nonmerged_handlers_EQ : CommaJoined<["-"], "fsanitize-nonmerged-handlers=">, Group<f_clang_Group>,
2552+
HelpText<"Enable non-merged handlers for specified sanitizers">;
2553+
def fno_sanitize_nonmerged_handlers_EQ : CommaJoined<["-"], "fno-sanitize-nonmerged-handlers=">, Group<f_clang_Group>,
2554+
HelpText<"Disable non-merged handlers for specified sanitizers">;
25512555
def fsanitize_trap : Flag<["-"], "fsanitize-trap">, Group<f_clang_Group>,
25522556
Alias<fsanitize_trap_EQ>, AliasArgs<["all"]>,
25532557
HelpText<"Enable trapping for all sanitizers">;

clang/include/clang/Driver/SanitizerArgs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class SanitizerArgs {
2525
SanitizerSet Sanitizers;
2626
SanitizerSet RecoverableSanitizers;
2727
SanitizerSet TrapSanitizers;
28+
SanitizerSet NonMergedHandlers;
2829

2930
std::vector<std::string> UserIgnorelistFiles;
3031
std::vector<std::string> SystemIgnorelistFiles;

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3546,7 +3546,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
35463546
ArrayRef<llvm::Value *> FnArgs,
35473547
SanitizerHandler CheckHandler,
35483548
CheckRecoverableKind RecoverKind, bool IsFatal,
3549-
llvm::BasicBlock *ContBB) {
3549+
llvm::BasicBlock *ContBB, bool NoMerge) {
35503550
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
35513551
std::optional<ApplyDebugLocation> DL;
35523552
if (!CGF.Builder.getCurrentDebugLocation()) {
@@ -3581,7 +3581,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
35813581
llvm::AttributeList::FunctionIndex, B),
35823582
/*Local=*/true);
35833583
llvm::CallInst *HandlerCall = CGF.EmitNounwindRuntimeCall(Fn, FnArgs);
3584-
bool NoMerge =
3584+
NoMerge = NoMerge ||
35853585
ClSanitizeDebugDeoptimization ||
35863586
!CGF.CGM.getCodeGenOpts().OptimizationLevel ||
35873587
(CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
@@ -3608,6 +3608,7 @@ void CodeGenFunction::EmitCheck(
36083608
llvm::Value *FatalCond = nullptr;
36093609
llvm::Value *RecoverableCond = nullptr;
36103610
llvm::Value *TrapCond = nullptr;
3611+
bool NoMerge = false;
36113612
for (int i = 0, n = Checked.size(); i < n; ++i) {
36123613
llvm::Value *Check = Checked[i].first;
36133614
// -fsanitize-trap= overrides -fsanitize-recover=.
@@ -3618,6 +3619,9 @@ void CodeGenFunction::EmitCheck(
36183619
? RecoverableCond
36193620
: FatalCond;
36203621
Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check;
3622+
3623+
if (CGM.getCodeGenOpts().SanitizeNonMergedHandlers.has(Checked[i].second))
3624+
NoMerge = true;
36213625
}
36223626

36233627
if (ClSanitizeGuardChecks) {
@@ -3632,7 +3636,7 @@ void CodeGenFunction::EmitCheck(
36323636
}
36333637

36343638
if (TrapCond)
3635-
EmitTrapCheck(TrapCond, CheckHandler);
3639+
EmitTrapCheck(TrapCond, CheckHandler, NoMerge);
36363640
if (!FatalCond && !RecoverableCond)
36373641
return;
36383642

@@ -3698,7 +3702,7 @@ void CodeGenFunction::EmitCheck(
36983702
// Simple case: we need to generate a single handler call, either
36993703
// fatal, or non-fatal.
37003704
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind,
3701-
(FatalCond != nullptr), Cont);
3705+
(FatalCond != nullptr), Cont, NoMerge);
37023706
} else {
37033707
// Emit two handler calls: first one for set of unrecoverable checks,
37043708
// another one for recoverable.
@@ -3708,10 +3712,10 @@ void CodeGenFunction::EmitCheck(
37083712
Builder.CreateCondBr(FatalCond, NonFatalHandlerBB, FatalHandlerBB);
37093713
EmitBlock(FatalHandlerBB);
37103714
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, true,
3711-
NonFatalHandlerBB);
3715+
NonFatalHandlerBB, NoMerge);
37123716
EmitBlock(NonFatalHandlerBB);
37133717
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, false,
3714-
Cont);
3718+
Cont, NoMerge);
37153719
}
37163720

37173721
EmitBlock(Cont);
@@ -3901,7 +3905,8 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
39013905
}
39023906

39033907
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
3904-
SanitizerHandler CheckHandlerID) {
3908+
SanitizerHandler CheckHandlerID,
3909+
bool NoMerge) {
39053910
llvm::BasicBlock *Cont = createBasicBlock("cont");
39063911

39073912
// If we're optimizing, collapse all calls to trap down to just one per
@@ -3911,9 +3916,10 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
39113916

39123917
llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];
39133918

3914-
bool NoMerge = ClSanitizeDebugDeoptimization ||
3915-
!CGM.getCodeGenOpts().OptimizationLevel ||
3916-
(CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
3919+
NoMerge = NoMerge ||
3920+
ClSanitizeDebugDeoptimization ||
3921+
!CGM.getCodeGenOpts().OptimizationLevel ||
3922+
(CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
39173923

39183924
if (TrapBB && !NoMerge) {
39193925
auto Call = TrapBB->begin();

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5166,7 +5166,7 @@ class CodeGenFunction : public CodeGenTypeCache {
51665166

51675167
/// Create a basic block that will call the trap intrinsic, and emit a
51685168
/// conditional branch to it, for the -ftrapv checks.
5169-
void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID);
5169+
void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID, bool NoMerge = false);
51705170

51715171
/// Emit a call to trap or debugtrap and attach function attribute
51725172
/// "trap-func-name" if specified.

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ static const SanitizerMask TrappingSupported =
6868
SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
6969
SanitizerKind::LocalBounds | SanitizerKind::CFI |
7070
SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast;
71+
static const SanitizerMask NonMergedDefault;
72+
static const SanitizerMask NonMergedSupported =
73+
(SanitizerKind::Undefined & ~SanitizerKind::Vptr);
7174
static const SanitizerMask TrappingDefault = SanitizerKind::CFI;
7275
static const SanitizerMask CFIClasses =
7376
SanitizerKind::CFIVCall | SanitizerKind::CFINVCall |
@@ -696,6 +699,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
696699
TrappingKinds &= Kinds;
697700
RecoverableKinds &= ~TrappingKinds;
698701

702+
// Parse -f(no-)?sanitize-nonmerged-handlers flags
703+
SanitizerMask AlwaysNonMerged; // Empty
704+
SanitizerMask NeverNonMerged = ~(setGroupBits(NonMergedSupported));
705+
SanitizerMask NonMergedKinds = parseSanitizeArgs(
706+
D, Args, DiagnoseErrors, NonMergedDefault, AlwaysNonMerged,
707+
NeverNonMerged, options::OPT_fsanitize_nonmerged_handlers_EQ,
708+
options::OPT_fno_sanitize_nonmerged_handlers_EQ);
709+
RecoverableKinds |= AlwaysNonMerged;
710+
RecoverableKinds &= ~NeverNonMerged;
711+
RecoverableKinds &= Kinds;
712+
699713
// Setup ignorelist files.
700714
// Add default ignorelist from resource directory for activated sanitizers,
701715
// and validate special case lists format.
@@ -1113,6 +1127,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
11131127
TrapSanitizers.Mask |= TrappingKinds;
11141128
assert(!(RecoverableKinds & TrappingKinds) &&
11151129
"Overlap between recoverable and trapping sanitizers");
1130+
1131+
NonMergedHandlers.Mask |= NonMergedKinds;
11161132
}
11171133

11181134
static std::string toString(const clang::SanitizerSet &Sanitizers) {
@@ -1274,6 +1290,10 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
12741290
CmdArgs.push_back(
12751291
Args.MakeArgString("-fsanitize-trap=" + toString(TrapSanitizers)));
12761292

1293+
if (!NonMergedHandlers.empty())
1294+
CmdArgs.push_back(Args.MakeArgString("-fsanitize-nonmerged-handlers=" +
1295+
toString(NonMergedHandlers)));
1296+
12771297
addSpecialCaseListOpt(Args, CmdArgs,
12781298
"-fsanitize-ignorelist=", UserIgnorelistFiles);
12791299
addSpecialCaseListOpt(Args, CmdArgs,
@@ -1446,7 +1466,9 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
14461466
A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
14471467
A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
14481468
A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
1449-
A->getOption().matches(options::OPT_fno_sanitize_trap_EQ)) &&
1469+
A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
1470+
A->getOption().matches(options::OPT_fsanitize_nonmerged_handlers_EQ) ||
1471+
A->getOption().matches(options::OPT_fno_sanitize_nonmerged_handlers_EQ)) &&
14501472
"Invalid argument in parseArgValues!");
14511473
SanitizerMask Kinds;
14521474
for (int i = 0, n = A->getNumValues(); i != n; ++i) {

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,6 +1792,9 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
17921792
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeTrap))
17931793
GenerateArg(Consumer, OPT_fsanitize_trap_EQ, Sanitizer);
17941794

1795+
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeNonMergedHandlers))
1796+
GenerateArg(Consumer, OPT_fsanitize_nonmerged_handlers_EQ, Sanitizer);
1797+
17951798
if (!Opts.EmitVersionIdentMetadata)
17961799
GenerateArg(Consumer, OPT_Qn);
17971800

@@ -2269,6 +2272,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
22692272
parseSanitizerKinds("-fsanitize-trap=",
22702273
Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags,
22712274
Opts.SanitizeTrap);
2275+
parseSanitizerKinds("-fsanitize-nonmerged-handlers=",
2276+
Args.getAllArgValues(OPT_fsanitize_nonmerged_handlers_EQ), Diags,
2277+
Opts.SanitizeNonMergedHandlers);
22722278

22732279
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
22742280

0 commit comments

Comments
 (0)