-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[ubsan] Add -fsanitize-merge (and -fno-sanitize-merge) #120464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…d-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.
✅ With the latest revision this PR passed the C/C++ code formatter. |
@llvm/pr-subscribers-clang-codegen Author: Thurston Dang (thurstond) Changes'-mllvm -ubsan-unique-traps' (#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 #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. Full diff: https://github.com/llvm/llvm-project/pull/120464.diff 9 Files Affected:
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 2dcf98b465661e..9b97adce42cc2a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -380,6 +380,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// Set of sanitizer checks that trap rather than diagnose.
SanitizerSet SanitizeTrap;
+ /// Set of sanitizer checks that have non-merged handlers (better
+ /// debuggability at the expense of code size).
+ SanitizerSet SanitizeNonMergedHandlers;
+
/// List of backend command-line options for -fembed-bitcode.
std::vector<uint8_t> CmdArgs;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 7b544d2534d469..e9fd59df3f9111 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2548,6 +2548,14 @@ def fsanitize_trap_EQ : CommaJoined<["-"], "fsanitize-trap=">, Group<f_clang_Gro
def fno_sanitize_trap_EQ : CommaJoined<["-"], "fno-sanitize-trap=">, Group<f_clang_Group>,
Visibility<[ClangOption, CLOption]>,
HelpText<"Disable trapping for specified sanitizers">;
+def fsanitize_nonmerged_handlers_EQ
+ : CommaJoined<["-"], "fsanitize-nonmerged-handlers=">,
+ Group<f_clang_Group>,
+ HelpText<"Enable non-merged handlers for specified sanitizers">;
+def fno_sanitize_nonmerged_handlers_EQ
+ : CommaJoined<["-"], "fno-sanitize-nonmerged-handlers=">,
+ Group<f_clang_Group>,
+ HelpText<"Disable non-merged handlers for specified sanitizers">;
def fsanitize_trap : Flag<["-"], "fsanitize-trap">, Group<f_clang_Group>,
Alias<fsanitize_trap_EQ>, AliasArgs<["all"]>,
HelpText<"Enable trapping for all sanitizers">;
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 4f08ea2b260179..28cfe72d6a34bd 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -25,6 +25,7 @@ class SanitizerArgs {
SanitizerSet Sanitizers;
SanitizerSet RecoverableSanitizers;
SanitizerSet TrapSanitizers;
+ SanitizerSet NonMergedHandlers;
std::vector<std::string> UserIgnorelistFiles;
std::vector<std::string> SystemIgnorelistFiles;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 79955f55714164..9c0ddda2e28d9d 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3546,7 +3546,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
ArrayRef<llvm::Value *> FnArgs,
SanitizerHandler CheckHandler,
CheckRecoverableKind RecoverKind, bool IsFatal,
- llvm::BasicBlock *ContBB) {
+ llvm::BasicBlock *ContBB, bool NoMerge) {
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
std::optional<ApplyDebugLocation> DL;
if (!CGF.Builder.getCurrentDebugLocation()) {
@@ -3581,10 +3581,9 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
llvm::AttributeList::FunctionIndex, B),
/*Local=*/true);
llvm::CallInst *HandlerCall = CGF.EmitNounwindRuntimeCall(Fn, FnArgs);
- bool NoMerge =
- ClSanitizeDebugDeoptimization ||
- !CGF.CGM.getCodeGenOpts().OptimizationLevel ||
- (CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
+ NoMerge = NoMerge || ClSanitizeDebugDeoptimization ||
+ !CGF.CGM.getCodeGenOpts().OptimizationLevel ||
+ (CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
if (!MayReturn) {
@@ -3608,6 +3607,7 @@ void CodeGenFunction::EmitCheck(
llvm::Value *FatalCond = nullptr;
llvm::Value *RecoverableCond = nullptr;
llvm::Value *TrapCond = nullptr;
+ bool NoMerge = false;
for (int i = 0, n = Checked.size(); i < n; ++i) {
llvm::Value *Check = Checked[i].first;
// -fsanitize-trap= overrides -fsanitize-recover=.
@@ -3618,6 +3618,9 @@ void CodeGenFunction::EmitCheck(
? RecoverableCond
: FatalCond;
Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check;
+
+ if (CGM.getCodeGenOpts().SanitizeNonMergedHandlers.has(Checked[i].second))
+ NoMerge = true;
}
if (ClSanitizeGuardChecks) {
@@ -3632,7 +3635,7 @@ void CodeGenFunction::EmitCheck(
}
if (TrapCond)
- EmitTrapCheck(TrapCond, CheckHandler);
+ EmitTrapCheck(TrapCond, CheckHandler, NoMerge);
if (!FatalCond && !RecoverableCond)
return;
@@ -3698,7 +3701,7 @@ void CodeGenFunction::EmitCheck(
// Simple case: we need to generate a single handler call, either
// fatal, or non-fatal.
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind,
- (FatalCond != nullptr), Cont);
+ (FatalCond != nullptr), Cont, NoMerge);
} else {
// Emit two handler calls: first one for set of unrecoverable checks,
// another one for recoverable.
@@ -3708,10 +3711,10 @@ void CodeGenFunction::EmitCheck(
Builder.CreateCondBr(FatalCond, NonFatalHandlerBB, FatalHandlerBB);
EmitBlock(FatalHandlerBB);
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, true,
- NonFatalHandlerBB);
+ NonFatalHandlerBB, NoMerge);
EmitBlock(NonFatalHandlerBB);
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, false,
- Cont);
+ Cont, NoMerge);
}
EmitBlock(Cont);
@@ -3901,7 +3904,8 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
}
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
- SanitizerHandler CheckHandlerID) {
+ SanitizerHandler CheckHandlerID,
+ bool NoMerge) {
llvm::BasicBlock *Cont = createBasicBlock("cont");
// If we're optimizing, collapse all calls to trap down to just one per
@@ -3911,9 +3915,9 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];
- bool NoMerge = ClSanitizeDebugDeoptimization ||
- !CGM.getCodeGenOpts().OptimizationLevel ||
- (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
+ NoMerge = NoMerge || ClSanitizeDebugDeoptimization ||
+ !CGM.getCodeGenOpts().OptimizationLevel ||
+ (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (TrapBB && !NoMerge) {
auto Call = TrapBB->begin();
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 092d55355a0a17..99a2a44248d274 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5166,7 +5166,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Create a basic block that will call the trap intrinsic, and emit a
/// conditional branch to it, for the -ftrapv checks.
- void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID);
+ void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID,
+ bool NoMerge = false);
/// Emit a call to trap or debugtrap and attach function attribute
/// "trap-func-name" if specified.
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 81f94f23873661..969d112e570474 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -68,6 +68,9 @@ static const SanitizerMask TrappingSupported =
SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
SanitizerKind::LocalBounds | SanitizerKind::CFI |
SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast;
+static const SanitizerMask NonMergedDefault;
+static const SanitizerMask NonMergedSupported =
+ (SanitizerKind::Undefined & ~SanitizerKind::Vptr);
static const SanitizerMask TrappingDefault = SanitizerKind::CFI;
static const SanitizerMask CFIClasses =
SanitizerKind::CFIVCall | SanitizerKind::CFINVCall |
@@ -696,6 +699,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
TrappingKinds &= Kinds;
RecoverableKinds &= ~TrappingKinds;
+ // Parse -f(no-)?sanitize-nonmerged-handlers flags
+ SanitizerMask AlwaysNonMerged; // Empty
+ SanitizerMask NeverNonMerged = ~(setGroupBits(NonMergedSupported));
+ SanitizerMask NonMergedKinds = parseSanitizeArgs(
+ D, Args, DiagnoseErrors, NonMergedDefault, AlwaysNonMerged,
+ NeverNonMerged, options::OPT_fsanitize_nonmerged_handlers_EQ,
+ options::OPT_fno_sanitize_nonmerged_handlers_EQ);
+ NonMergedKinds |= AlwaysNonMerged;
+ NonMergedKinds &= ~NeverNonMerged;
+ NonMergedKinds &= Kinds;
+
// Setup ignorelist files.
// Add default ignorelist from resource directory for activated sanitizers,
// and validate special case lists format.
@@ -1113,6 +1127,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
TrapSanitizers.Mask |= TrappingKinds;
assert(!(RecoverableKinds & TrappingKinds) &&
"Overlap between recoverable and trapping sanitizers");
+
+ NonMergedHandlers.Mask |= NonMergedKinds;
}
static std::string toString(const clang::SanitizerSet &Sanitizers) {
@@ -1274,6 +1290,10 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
CmdArgs.push_back(
Args.MakeArgString("-fsanitize-trap=" + toString(TrapSanitizers)));
+ if (!NonMergedHandlers.empty())
+ CmdArgs.push_back(Args.MakeArgString("-fsanitize-nonmerged-handlers=" +
+ toString(NonMergedHandlers)));
+
addSpecialCaseListOpt(Args, CmdArgs,
"-fsanitize-ignorelist=", UserIgnorelistFiles);
addSpecialCaseListOpt(Args, CmdArgs,
@@ -1441,13 +1461,17 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors) {
- assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
- A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
- A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_trap_EQ)) &&
- "Invalid argument in parseArgValues!");
+ assert(
+ (A->getOption().matches(options::OPT_fsanitize_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_nonmerged_handlers_EQ) ||
+ A->getOption().matches(
+ options::OPT_fno_sanitize_nonmerged_handlers_EQ)) &&
+ "Invalid argument in parseArgValues!");
SanitizerMask Kinds;
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
const char *Value = A->getValue(i);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 298fafc21588a1..7fa01292f532c0 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1792,6 +1792,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeTrap))
GenerateArg(Consumer, OPT_fsanitize_trap_EQ, Sanitizer);
+ for (StringRef Sanitizer :
+ serializeSanitizerKinds(Opts.SanitizeNonMergedHandlers))
+ GenerateArg(Consumer, OPT_fsanitize_nonmerged_handlers_EQ, Sanitizer);
+
if (!Opts.EmitVersionIdentMetadata)
GenerateArg(Consumer, OPT_Qn);
@@ -2269,6 +2273,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
parseSanitizerKinds("-fsanitize-trap=",
Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags,
Opts.SanitizeTrap);
+ parseSanitizerKinds("-fsanitize-nonmerged-handlers=",
+ Args.getAllArgValues(OPT_fsanitize_nonmerged_handlers_EQ),
+ Diags, Opts.SanitizeNonMergedHandlers);
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
diff --git a/clang/test/CodeGen/ubsan-trap-merge.c b/clang/test/CodeGen/ubsan-trap-merge.c
index 412ec7b09744ef..3a5a551fa7afa6 100644
--- a/clang/test/CodeGen/ubsan-trap-merge.c
+++ b/clang/test/CodeGen/ubsan-trap-merge.c
@@ -1,11 +1,16 @@
// NOTE: Assertions have mostly been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// The most important assertion is the attributes at the end of the file, which
-// shows whether -ubsan-unique-traps attaches 'nomerge' to each ubsan call.
+// shows whether -ubsan-unique-traps and -fsanitize-nonmerged-handlers attach
+// 'nomerge' to each ubsan call.
//
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefix=TRAP
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - | FileCheck %s --check-prefix=HANDLER
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefix=MINRT
//
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefix=TRAP
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - | FileCheck %s --check-prefix=HANDLER
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefix=MINRT
+//
// REQUIRES: x86-registered-target
// TRAP-LABEL: define dso_local range(i32 -2147483523, -2147483648) i32 @f(
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index bb692b2aeea1d3..2d34ae871b6f55 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -9,6 +9,11 @@
// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-trap=undefined -fsanitize-nonmerged-handlers=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-NONMERGED
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-trap=undefined -fsanitize-nonmerged-handlers=undefined -fno-sanitize-nonmerged-handlers=signed-integer-overflow %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-NONMERGED2
+// CHECK-UNDEFINED-NONMERGED: "-fsanitize-nonmerged-handlers=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-NONMERGED2: "-fsanitize-nonmerged-handlers=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
|
@llvm/pr-subscribers-clang-driver Author: Thurston Dang (thurstond) Changes'-mllvm -ubsan-unique-traps' (#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 #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. Full diff: https://github.com/llvm/llvm-project/pull/120464.diff 9 Files Affected:
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 2dcf98b465661e..9b97adce42cc2a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -380,6 +380,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// Set of sanitizer checks that trap rather than diagnose.
SanitizerSet SanitizeTrap;
+ /// Set of sanitizer checks that have non-merged handlers (better
+ /// debuggability at the expense of code size).
+ SanitizerSet SanitizeNonMergedHandlers;
+
/// List of backend command-line options for -fembed-bitcode.
std::vector<uint8_t> CmdArgs;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 7b544d2534d469..e9fd59df3f9111 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2548,6 +2548,14 @@ def fsanitize_trap_EQ : CommaJoined<["-"], "fsanitize-trap=">, Group<f_clang_Gro
def fno_sanitize_trap_EQ : CommaJoined<["-"], "fno-sanitize-trap=">, Group<f_clang_Group>,
Visibility<[ClangOption, CLOption]>,
HelpText<"Disable trapping for specified sanitizers">;
+def fsanitize_nonmerged_handlers_EQ
+ : CommaJoined<["-"], "fsanitize-nonmerged-handlers=">,
+ Group<f_clang_Group>,
+ HelpText<"Enable non-merged handlers for specified sanitizers">;
+def fno_sanitize_nonmerged_handlers_EQ
+ : CommaJoined<["-"], "fno-sanitize-nonmerged-handlers=">,
+ Group<f_clang_Group>,
+ HelpText<"Disable non-merged handlers for specified sanitizers">;
def fsanitize_trap : Flag<["-"], "fsanitize-trap">, Group<f_clang_Group>,
Alias<fsanitize_trap_EQ>, AliasArgs<["all"]>,
HelpText<"Enable trapping for all sanitizers">;
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 4f08ea2b260179..28cfe72d6a34bd 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -25,6 +25,7 @@ class SanitizerArgs {
SanitizerSet Sanitizers;
SanitizerSet RecoverableSanitizers;
SanitizerSet TrapSanitizers;
+ SanitizerSet NonMergedHandlers;
std::vector<std::string> UserIgnorelistFiles;
std::vector<std::string> SystemIgnorelistFiles;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 79955f55714164..9c0ddda2e28d9d 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3546,7 +3546,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
ArrayRef<llvm::Value *> FnArgs,
SanitizerHandler CheckHandler,
CheckRecoverableKind RecoverKind, bool IsFatal,
- llvm::BasicBlock *ContBB) {
+ llvm::BasicBlock *ContBB, bool NoMerge) {
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
std::optional<ApplyDebugLocation> DL;
if (!CGF.Builder.getCurrentDebugLocation()) {
@@ -3581,10 +3581,9 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
llvm::AttributeList::FunctionIndex, B),
/*Local=*/true);
llvm::CallInst *HandlerCall = CGF.EmitNounwindRuntimeCall(Fn, FnArgs);
- bool NoMerge =
- ClSanitizeDebugDeoptimization ||
- !CGF.CGM.getCodeGenOpts().OptimizationLevel ||
- (CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
+ NoMerge = NoMerge || ClSanitizeDebugDeoptimization ||
+ !CGF.CGM.getCodeGenOpts().OptimizationLevel ||
+ (CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
if (!MayReturn) {
@@ -3608,6 +3607,7 @@ void CodeGenFunction::EmitCheck(
llvm::Value *FatalCond = nullptr;
llvm::Value *RecoverableCond = nullptr;
llvm::Value *TrapCond = nullptr;
+ bool NoMerge = false;
for (int i = 0, n = Checked.size(); i < n; ++i) {
llvm::Value *Check = Checked[i].first;
// -fsanitize-trap= overrides -fsanitize-recover=.
@@ -3618,6 +3618,9 @@ void CodeGenFunction::EmitCheck(
? RecoverableCond
: FatalCond;
Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check;
+
+ if (CGM.getCodeGenOpts().SanitizeNonMergedHandlers.has(Checked[i].second))
+ NoMerge = true;
}
if (ClSanitizeGuardChecks) {
@@ -3632,7 +3635,7 @@ void CodeGenFunction::EmitCheck(
}
if (TrapCond)
- EmitTrapCheck(TrapCond, CheckHandler);
+ EmitTrapCheck(TrapCond, CheckHandler, NoMerge);
if (!FatalCond && !RecoverableCond)
return;
@@ -3698,7 +3701,7 @@ void CodeGenFunction::EmitCheck(
// Simple case: we need to generate a single handler call, either
// fatal, or non-fatal.
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind,
- (FatalCond != nullptr), Cont);
+ (FatalCond != nullptr), Cont, NoMerge);
} else {
// Emit two handler calls: first one for set of unrecoverable checks,
// another one for recoverable.
@@ -3708,10 +3711,10 @@ void CodeGenFunction::EmitCheck(
Builder.CreateCondBr(FatalCond, NonFatalHandlerBB, FatalHandlerBB);
EmitBlock(FatalHandlerBB);
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, true,
- NonFatalHandlerBB);
+ NonFatalHandlerBB, NoMerge);
EmitBlock(NonFatalHandlerBB);
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, false,
- Cont);
+ Cont, NoMerge);
}
EmitBlock(Cont);
@@ -3901,7 +3904,8 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
}
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
- SanitizerHandler CheckHandlerID) {
+ SanitizerHandler CheckHandlerID,
+ bool NoMerge) {
llvm::BasicBlock *Cont = createBasicBlock("cont");
// If we're optimizing, collapse all calls to trap down to just one per
@@ -3911,9 +3915,9 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];
- bool NoMerge = ClSanitizeDebugDeoptimization ||
- !CGM.getCodeGenOpts().OptimizationLevel ||
- (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
+ NoMerge = NoMerge || ClSanitizeDebugDeoptimization ||
+ !CGM.getCodeGenOpts().OptimizationLevel ||
+ (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (TrapBB && !NoMerge) {
auto Call = TrapBB->begin();
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 092d55355a0a17..99a2a44248d274 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5166,7 +5166,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Create a basic block that will call the trap intrinsic, and emit a
/// conditional branch to it, for the -ftrapv checks.
- void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID);
+ void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID,
+ bool NoMerge = false);
/// Emit a call to trap or debugtrap and attach function attribute
/// "trap-func-name" if specified.
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 81f94f23873661..969d112e570474 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -68,6 +68,9 @@ static const SanitizerMask TrappingSupported =
SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
SanitizerKind::LocalBounds | SanitizerKind::CFI |
SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast;
+static const SanitizerMask NonMergedDefault;
+static const SanitizerMask NonMergedSupported =
+ (SanitizerKind::Undefined & ~SanitizerKind::Vptr);
static const SanitizerMask TrappingDefault = SanitizerKind::CFI;
static const SanitizerMask CFIClasses =
SanitizerKind::CFIVCall | SanitizerKind::CFINVCall |
@@ -696,6 +699,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
TrappingKinds &= Kinds;
RecoverableKinds &= ~TrappingKinds;
+ // Parse -f(no-)?sanitize-nonmerged-handlers flags
+ SanitizerMask AlwaysNonMerged; // Empty
+ SanitizerMask NeverNonMerged = ~(setGroupBits(NonMergedSupported));
+ SanitizerMask NonMergedKinds = parseSanitizeArgs(
+ D, Args, DiagnoseErrors, NonMergedDefault, AlwaysNonMerged,
+ NeverNonMerged, options::OPT_fsanitize_nonmerged_handlers_EQ,
+ options::OPT_fno_sanitize_nonmerged_handlers_EQ);
+ NonMergedKinds |= AlwaysNonMerged;
+ NonMergedKinds &= ~NeverNonMerged;
+ NonMergedKinds &= Kinds;
+
// Setup ignorelist files.
// Add default ignorelist from resource directory for activated sanitizers,
// and validate special case lists format.
@@ -1113,6 +1127,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
TrapSanitizers.Mask |= TrappingKinds;
assert(!(RecoverableKinds & TrappingKinds) &&
"Overlap between recoverable and trapping sanitizers");
+
+ NonMergedHandlers.Mask |= NonMergedKinds;
}
static std::string toString(const clang::SanitizerSet &Sanitizers) {
@@ -1274,6 +1290,10 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
CmdArgs.push_back(
Args.MakeArgString("-fsanitize-trap=" + toString(TrapSanitizers)));
+ if (!NonMergedHandlers.empty())
+ CmdArgs.push_back(Args.MakeArgString("-fsanitize-nonmerged-handlers=" +
+ toString(NonMergedHandlers)));
+
addSpecialCaseListOpt(Args, CmdArgs,
"-fsanitize-ignorelist=", UserIgnorelistFiles);
addSpecialCaseListOpt(Args, CmdArgs,
@@ -1441,13 +1461,17 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors) {
- assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
- A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
- A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_trap_EQ)) &&
- "Invalid argument in parseArgValues!");
+ assert(
+ (A->getOption().matches(options::OPT_fsanitize_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_nonmerged_handlers_EQ) ||
+ A->getOption().matches(
+ options::OPT_fno_sanitize_nonmerged_handlers_EQ)) &&
+ "Invalid argument in parseArgValues!");
SanitizerMask Kinds;
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
const char *Value = A->getValue(i);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 298fafc21588a1..7fa01292f532c0 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1792,6 +1792,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeTrap))
GenerateArg(Consumer, OPT_fsanitize_trap_EQ, Sanitizer);
+ for (StringRef Sanitizer :
+ serializeSanitizerKinds(Opts.SanitizeNonMergedHandlers))
+ GenerateArg(Consumer, OPT_fsanitize_nonmerged_handlers_EQ, Sanitizer);
+
if (!Opts.EmitVersionIdentMetadata)
GenerateArg(Consumer, OPT_Qn);
@@ -2269,6 +2273,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
parseSanitizerKinds("-fsanitize-trap=",
Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags,
Opts.SanitizeTrap);
+ parseSanitizerKinds("-fsanitize-nonmerged-handlers=",
+ Args.getAllArgValues(OPT_fsanitize_nonmerged_handlers_EQ),
+ Diags, Opts.SanitizeNonMergedHandlers);
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
diff --git a/clang/test/CodeGen/ubsan-trap-merge.c b/clang/test/CodeGen/ubsan-trap-merge.c
index 412ec7b09744ef..3a5a551fa7afa6 100644
--- a/clang/test/CodeGen/ubsan-trap-merge.c
+++ b/clang/test/CodeGen/ubsan-trap-merge.c
@@ -1,11 +1,16 @@
// NOTE: Assertions have mostly been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// The most important assertion is the attributes at the end of the file, which
-// shows whether -ubsan-unique-traps attaches 'nomerge' to each ubsan call.
+// shows whether -ubsan-unique-traps and -fsanitize-nonmerged-handlers attach
+// 'nomerge' to each ubsan call.
//
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefix=TRAP
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - | FileCheck %s --check-prefix=HANDLER
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefix=MINRT
//
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefix=TRAP
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - | FileCheck %s --check-prefix=HANDLER
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefix=MINRT
+//
// REQUIRES: x86-registered-target
// TRAP-LABEL: define dso_local range(i32 -2147483523, -2147483648) i32 @f(
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index bb692b2aeea1d3..2d34ae871b6f55 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -9,6 +9,11 @@
// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-trap=undefined -fsanitize-nonmerged-handlers=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-NONMERGED
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-trap=undefined -fsanitize-nonmerged-handlers=undefined -fno-sanitize-nonmerged-handlers=signed-integer-overflow %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-NONMERGED2
+// CHECK-UNDEFINED-NONMERGED: "-fsanitize-nonmerged-handlers=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-NONMERGED2: "-fsanitize-nonmerged-handlers=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
|
@llvm/pr-subscribers-clang Author: Thurston Dang (thurstond) Changes'-mllvm -ubsan-unique-traps' (#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 #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. Full diff: https://github.com/llvm/llvm-project/pull/120464.diff 9 Files Affected:
diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h
index 2dcf98b465661e..9b97adce42cc2a 100644
--- a/clang/include/clang/Basic/CodeGenOptions.h
+++ b/clang/include/clang/Basic/CodeGenOptions.h
@@ -380,6 +380,10 @@ class CodeGenOptions : public CodeGenOptionsBase {
/// Set of sanitizer checks that trap rather than diagnose.
SanitizerSet SanitizeTrap;
+ /// Set of sanitizer checks that have non-merged handlers (better
+ /// debuggability at the expense of code size).
+ SanitizerSet SanitizeNonMergedHandlers;
+
/// List of backend command-line options for -fembed-bitcode.
std::vector<uint8_t> CmdArgs;
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 7b544d2534d469..e9fd59df3f9111 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -2548,6 +2548,14 @@ def fsanitize_trap_EQ : CommaJoined<["-"], "fsanitize-trap=">, Group<f_clang_Gro
def fno_sanitize_trap_EQ : CommaJoined<["-"], "fno-sanitize-trap=">, Group<f_clang_Group>,
Visibility<[ClangOption, CLOption]>,
HelpText<"Disable trapping for specified sanitizers">;
+def fsanitize_nonmerged_handlers_EQ
+ : CommaJoined<["-"], "fsanitize-nonmerged-handlers=">,
+ Group<f_clang_Group>,
+ HelpText<"Enable non-merged handlers for specified sanitizers">;
+def fno_sanitize_nonmerged_handlers_EQ
+ : CommaJoined<["-"], "fno-sanitize-nonmerged-handlers=">,
+ Group<f_clang_Group>,
+ HelpText<"Disable non-merged handlers for specified sanitizers">;
def fsanitize_trap : Flag<["-"], "fsanitize-trap">, Group<f_clang_Group>,
Alias<fsanitize_trap_EQ>, AliasArgs<["all"]>,
HelpText<"Enable trapping for all sanitizers">;
diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h
index 4f08ea2b260179..28cfe72d6a34bd 100644
--- a/clang/include/clang/Driver/SanitizerArgs.h
+++ b/clang/include/clang/Driver/SanitizerArgs.h
@@ -25,6 +25,7 @@ class SanitizerArgs {
SanitizerSet Sanitizers;
SanitizerSet RecoverableSanitizers;
SanitizerSet TrapSanitizers;
+ SanitizerSet NonMergedHandlers;
std::vector<std::string> UserIgnorelistFiles;
std::vector<std::string> SystemIgnorelistFiles;
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 79955f55714164..9c0ddda2e28d9d 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3546,7 +3546,7 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
ArrayRef<llvm::Value *> FnArgs,
SanitizerHandler CheckHandler,
CheckRecoverableKind RecoverKind, bool IsFatal,
- llvm::BasicBlock *ContBB) {
+ llvm::BasicBlock *ContBB, bool NoMerge) {
assert(IsFatal || RecoverKind != CheckRecoverableKind::Unrecoverable);
std::optional<ApplyDebugLocation> DL;
if (!CGF.Builder.getCurrentDebugLocation()) {
@@ -3581,10 +3581,9 @@ static void emitCheckHandlerCall(CodeGenFunction &CGF,
llvm::AttributeList::FunctionIndex, B),
/*Local=*/true);
llvm::CallInst *HandlerCall = CGF.EmitNounwindRuntimeCall(Fn, FnArgs);
- bool NoMerge =
- ClSanitizeDebugDeoptimization ||
- !CGF.CGM.getCodeGenOpts().OptimizationLevel ||
- (CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
+ NoMerge = NoMerge || ClSanitizeDebugDeoptimization ||
+ !CGF.CGM.getCodeGenOpts().OptimizationLevel ||
+ (CGF.CurCodeDecl && CGF.CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (NoMerge)
HandlerCall->addFnAttr(llvm::Attribute::NoMerge);
if (!MayReturn) {
@@ -3608,6 +3607,7 @@ void CodeGenFunction::EmitCheck(
llvm::Value *FatalCond = nullptr;
llvm::Value *RecoverableCond = nullptr;
llvm::Value *TrapCond = nullptr;
+ bool NoMerge = false;
for (int i = 0, n = Checked.size(); i < n; ++i) {
llvm::Value *Check = Checked[i].first;
// -fsanitize-trap= overrides -fsanitize-recover=.
@@ -3618,6 +3618,9 @@ void CodeGenFunction::EmitCheck(
? RecoverableCond
: FatalCond;
Cond = Cond ? Builder.CreateAnd(Cond, Check) : Check;
+
+ if (CGM.getCodeGenOpts().SanitizeNonMergedHandlers.has(Checked[i].second))
+ NoMerge = true;
}
if (ClSanitizeGuardChecks) {
@@ -3632,7 +3635,7 @@ void CodeGenFunction::EmitCheck(
}
if (TrapCond)
- EmitTrapCheck(TrapCond, CheckHandler);
+ EmitTrapCheck(TrapCond, CheckHandler, NoMerge);
if (!FatalCond && !RecoverableCond)
return;
@@ -3698,7 +3701,7 @@ void CodeGenFunction::EmitCheck(
// Simple case: we need to generate a single handler call, either
// fatal, or non-fatal.
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind,
- (FatalCond != nullptr), Cont);
+ (FatalCond != nullptr), Cont, NoMerge);
} else {
// Emit two handler calls: first one for set of unrecoverable checks,
// another one for recoverable.
@@ -3708,10 +3711,10 @@ void CodeGenFunction::EmitCheck(
Builder.CreateCondBr(FatalCond, NonFatalHandlerBB, FatalHandlerBB);
EmitBlock(FatalHandlerBB);
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, true,
- NonFatalHandlerBB);
+ NonFatalHandlerBB, NoMerge);
EmitBlock(NonFatalHandlerBB);
emitCheckHandlerCall(*this, FnType, Args, CheckHandler, RecoverKind, false,
- Cont);
+ Cont, NoMerge);
}
EmitBlock(Cont);
@@ -3901,7 +3904,8 @@ void CodeGenFunction::EmitUnreachable(SourceLocation Loc) {
}
void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
- SanitizerHandler CheckHandlerID) {
+ SanitizerHandler CheckHandlerID,
+ bool NoMerge) {
llvm::BasicBlock *Cont = createBasicBlock("cont");
// If we're optimizing, collapse all calls to trap down to just one per
@@ -3911,9 +3915,9 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked,
llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID];
- bool NoMerge = ClSanitizeDebugDeoptimization ||
- !CGM.getCodeGenOpts().OptimizationLevel ||
- (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
+ NoMerge = NoMerge || ClSanitizeDebugDeoptimization ||
+ !CGM.getCodeGenOpts().OptimizationLevel ||
+ (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>());
if (TrapBB && !NoMerge) {
auto Call = TrapBB->begin();
diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 092d55355a0a17..99a2a44248d274 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -5166,7 +5166,8 @@ class CodeGenFunction : public CodeGenTypeCache {
/// Create a basic block that will call the trap intrinsic, and emit a
/// conditional branch to it, for the -ftrapv checks.
- void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID);
+ void EmitTrapCheck(llvm::Value *Checked, SanitizerHandler CheckHandlerID,
+ bool NoMerge = false);
/// Emit a call to trap or debugtrap and attach function attribute
/// "trap-func-name" if specified.
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 81f94f23873661..969d112e570474 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -68,6 +68,9 @@ static const SanitizerMask TrappingSupported =
SanitizerKind::ImplicitConversion | SanitizerKind::Nullability |
SanitizerKind::LocalBounds | SanitizerKind::CFI |
SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast;
+static const SanitizerMask NonMergedDefault;
+static const SanitizerMask NonMergedSupported =
+ (SanitizerKind::Undefined & ~SanitizerKind::Vptr);
static const SanitizerMask TrappingDefault = SanitizerKind::CFI;
static const SanitizerMask CFIClasses =
SanitizerKind::CFIVCall | SanitizerKind::CFINVCall |
@@ -696,6 +699,17 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
TrappingKinds &= Kinds;
RecoverableKinds &= ~TrappingKinds;
+ // Parse -f(no-)?sanitize-nonmerged-handlers flags
+ SanitizerMask AlwaysNonMerged; // Empty
+ SanitizerMask NeverNonMerged = ~(setGroupBits(NonMergedSupported));
+ SanitizerMask NonMergedKinds = parseSanitizeArgs(
+ D, Args, DiagnoseErrors, NonMergedDefault, AlwaysNonMerged,
+ NeverNonMerged, options::OPT_fsanitize_nonmerged_handlers_EQ,
+ options::OPT_fno_sanitize_nonmerged_handlers_EQ);
+ NonMergedKinds |= AlwaysNonMerged;
+ NonMergedKinds &= ~NeverNonMerged;
+ NonMergedKinds &= Kinds;
+
// Setup ignorelist files.
// Add default ignorelist from resource directory for activated sanitizers,
// and validate special case lists format.
@@ -1113,6 +1127,8 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
TrapSanitizers.Mask |= TrappingKinds;
assert(!(RecoverableKinds & TrappingKinds) &&
"Overlap between recoverable and trapping sanitizers");
+
+ NonMergedHandlers.Mask |= NonMergedKinds;
}
static std::string toString(const clang::SanitizerSet &Sanitizers) {
@@ -1274,6 +1290,10 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
CmdArgs.push_back(
Args.MakeArgString("-fsanitize-trap=" + toString(TrapSanitizers)));
+ if (!NonMergedHandlers.empty())
+ CmdArgs.push_back(Args.MakeArgString("-fsanitize-nonmerged-handlers=" +
+ toString(NonMergedHandlers)));
+
addSpecialCaseListOpt(Args, CmdArgs,
"-fsanitize-ignorelist=", UserIgnorelistFiles);
addSpecialCaseListOpt(Args, CmdArgs,
@@ -1441,13 +1461,17 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
bool DiagnoseErrors) {
- assert((A->getOption().matches(options::OPT_fsanitize_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
- A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
- A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
- A->getOption().matches(options::OPT_fno_sanitize_trap_EQ)) &&
- "Invalid argument in parseArgValues!");
+ assert(
+ (A->getOption().matches(options::OPT_fsanitize_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_recover_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_recover_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_trap_EQ) ||
+ A->getOption().matches(options::OPT_fno_sanitize_trap_EQ) ||
+ A->getOption().matches(options::OPT_fsanitize_nonmerged_handlers_EQ) ||
+ A->getOption().matches(
+ options::OPT_fno_sanitize_nonmerged_handlers_EQ)) &&
+ "Invalid argument in parseArgValues!");
SanitizerMask Kinds;
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
const char *Value = A->getValue(i);
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp
index 298fafc21588a1..7fa01292f532c0 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -1792,6 +1792,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts,
for (StringRef Sanitizer : serializeSanitizerKinds(Opts.SanitizeTrap))
GenerateArg(Consumer, OPT_fsanitize_trap_EQ, Sanitizer);
+ for (StringRef Sanitizer :
+ serializeSanitizerKinds(Opts.SanitizeNonMergedHandlers))
+ GenerateArg(Consumer, OPT_fsanitize_nonmerged_handlers_EQ, Sanitizer);
+
if (!Opts.EmitVersionIdentMetadata)
GenerateArg(Consumer, OPT_Qn);
@@ -2269,6 +2273,9 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args,
parseSanitizerKinds("-fsanitize-trap=",
Args.getAllArgValues(OPT_fsanitize_trap_EQ), Diags,
Opts.SanitizeTrap);
+ parseSanitizerKinds("-fsanitize-nonmerged-handlers=",
+ Args.getAllArgValues(OPT_fsanitize_nonmerged_handlers_EQ),
+ Diags, Opts.SanitizeNonMergedHandlers);
Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true);
diff --git a/clang/test/CodeGen/ubsan-trap-merge.c b/clang/test/CodeGen/ubsan-trap-merge.c
index 412ec7b09744ef..3a5a551fa7afa6 100644
--- a/clang/test/CodeGen/ubsan-trap-merge.c
+++ b/clang/test/CodeGen/ubsan-trap-merge.c
@@ -1,11 +1,16 @@
// NOTE: Assertions have mostly been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
// The most important assertion is the attributes at the end of the file, which
-// shows whether -ubsan-unique-traps attaches 'nomerge' to each ubsan call.
+// shows whether -ubsan-unique-traps and -fsanitize-nonmerged-handlers attach
+// 'nomerge' to each ubsan call.
//
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefix=TRAP
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - | FileCheck %s --check-prefix=HANDLER
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -mllvm -ubsan-unique-traps %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefix=MINRT
//
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - -fsanitize-trap=signed-integer-overflow | FileCheck %s --check-prefix=TRAP
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - | FileCheck %s --check-prefix=HANDLER
+// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -fsanitize=signed-integer-overflow -O3 -fsanitize-nonmerged-handlers=signed-integer-overflow %s -o - -fsanitize-minimal-runtime | FileCheck %s --check-prefix=MINRT
+//
// REQUIRES: x86-registered-target
// TRAP-LABEL: define dso_local range(i32 -2147483523, -2147483648) i32 @f(
diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c
index bb692b2aeea1d3..2d34ae871b6f55 100644
--- a/clang/test/Driver/fsanitize.c
+++ b/clang/test/Driver/fsanitize.c
@@ -9,6 +9,11 @@
// CHECK-UNDEFINED-TRAP: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
// CHECK-UNDEFINED-TRAP2: "-fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-trap=undefined -fsanitize-nonmerged-handlers=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-NONMERGED
+// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined -fsanitize-trap=undefined -fsanitize-nonmerged-handlers=undefined -fno-sanitize-nonmerged-handlers=signed-integer-overflow %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-NONMERGED2
+// CHECK-UNDEFINED-NONMERGED: "-fsanitize-nonmerged-handlers=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound"
+// CHECK-UNDEFINED-NONMERGED2: "-fsanitize-nonmerged-handlers=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,unreachable,vla-bound"
+
// RUN: %clang --target=x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|function|shift-base|shift-exponent|unreachable|return|vla-bound|alignment|null|vptr|pointer-overflow|float-cast-overflow|array-bounds|enum|bool|builtin|returns-nonnull-attribute|nonnull-attribute),?){19}"}}
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/144/builds/14299 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/140/builds/13376 Here is the relevant piece of the build log for the reference
|
…)" This reverts commit 7eaf470. Reason: buildbot breakage (e.g., https://lab.llvm.org/buildbot/#/builders/144/builds/14299/steps/6/logs/FAIL__Clang__ubsan-trap-debugloc_c)
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/133/builds/8646 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/190/builds/11649 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/60/builds/15574 Here is the relevant piece of the build log for the reference
|
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/56/builds/14864 Here is the relevant piece of the build log for the reference
|
…#120464)" This reverts commit 2691b96. This reapply fixes the buildbot breakage of the original patch, by updating clang/test/CodeGen/ubsan-trap-debugloc.c to specify -fsanitize-merge (the default, which is merge, is applied by the driver but not clang_cc1). This reapply also expands clang/test/CodeGen/ubsan-trap-merge.c. Original commit message: '-mllvm -ubsan-unique-traps' (llvm#65972) applies to all UBSan checks. This patch introduces -fsanitize-merge (defaults to on, maintaining the status quo behavior) and -fno-sanitize-merge (equivalent to '-mllvm -ubsan-unique-traps'), with the option to selectively applying non-merged handlers to a subset of UBSan checks (e.g., -fno-sanitize-merge=bool,enum). N.B. we do not use "trap" in the argument name since llvm#119302 has generalized -ubsan-unique-traps to work for non-trap modes (min-rt and regular rt). This patch does not remove the -ubsan-unique-traps flag; that will override -f(no-)sanitize-merge.
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/153/builds/18012 Here is the relevant piece of the build log for the reference
|
llvm#120464 (and earlier CLs) added -fsanitize-merge functionality, which is intended to work for all "sanitizers". It is nearly correct for CFI. This patch precommits some tests for CFI, to track the progress of future -fsanitize-merge fixes.
#120464 (and earlier CLs) added -fsanitize-merge functionality, which is intended to work for all "sanitizers". It is nearly correct for CFI. This patch precommits some tests for CFI, to track the progress of future -fsanitize-merge fixes for CFI.
…(#135104) llvm/llvm-project#120464 (and earlier CLs) added -fsanitize-merge functionality, which is intended to work for all "sanitizers". It is nearly correct for CFI. This patch precommits some tests for CFI, to track the progress of future -fsanitize-merge fixes for CFI.
llvm#120464 (and earlier CLs) added -fsanitize-merge functionality, which is intended to work for all "sanitizers". It is nearly correct for CFI. This patch precommits some tests for CFI, to track the progress of future -fsanitize-merge fixes for CFI.
-fno-sanitize-merge (introduced in llvm#120464) nearly works for CFI: code that calls EmitCheck will already check the merge options. This patch fixes one EmitTrapCheck call, which did not check the merge options, and for two other EmitTrapChecks, adds two TODOs that explain why it is difficult to fix them.
llvm#120464 (and earlier CLs) added -fsanitize-merge functionality, which is intended to work for all "sanitizers". It is nearly correct for CFI. This patch precommits some tests for CFI, to track the progress of future -fsanitize-merge fixes for CFI.
-fno-sanitize-merge (introduced in #120464) nearly works for CFI: code that calls EmitCheck will already check the merge options. This patch fixes one EmitTrapCheck call, which did not check the merge options, and for two other EmitTrapChecks, adds two TODOs that explain why it is difficult to fix them.
…(#135438) -fno-sanitize-merge (introduced in llvm/llvm-project#120464) nearly works for CFI: code that calls EmitCheck will already check the merge options. This patch fixes one EmitTrapCheck call, which did not check the merge options, and for two other EmitTrapChecks, adds two TODOs that explain why it is difficult to fix them.
-fno-sanitize-merge (introduced in llvm#120464) nearly works for CFI: code that calls EmitCheck will already check the merge options. This patch fixes one EmitTrapCheck call, which did not check the merge options, and for two other EmitTrapChecks, adds two TODOs that explain why it is difficult to fix them.
-fno-sanitize-merge (introduced in llvm#120464) nearly works for CFI: code that calls EmitCheck will already check the merge options. This patch fixes one EmitTrapCheck call, which did not check the merge options, and for two other EmitTrapChecks, adds two TODOs that explain why it is difficult to fix them.
-fno-sanitize-merge (introduced in llvm#120464) nearly works for CFI: code that calls EmitCheck will already check the merge options. This patch fixes one EmitTrapCheck call, which did not check the merge options, and for two other EmitTrapChecks, adds two TODOs that explain why it is difficult to fix them.
-fno-sanitize-merge (introduced in llvm#120464) nearly works for CFI: code that calls EmitCheck will already check the merge options. This patch fixes one EmitTrapCheck call, which did not check the merge options, and for two other EmitTrapChecks, adds two TODOs that explain why it is difficult to fix them.
'-mllvm -ubsan-unique-traps' (#65972) applies to all UBSan checks. This patch introduces -fsanitize-merge (defaults to on, maintaining the status quo behavior) and -fno-sanitize-merge (equivalent to '-mllvm -ubsan-unique-traps'), with the option to selectively applying non-merged handlers to a subset of UBSan checks (e.g., -fno-sanitize-merge=bool,enum).
N.B. we do not use "trap" in the argument name since #119302 has generalized -ubsan-unique-traps to work for non-trap modes (min-rt and regular rt).
This patch does not remove the -ubsan-unique-traps flag; that will override -f(no-)sanitize-merge.