Skip to content

Commit 821ce7a

Browse files
committed
add support for comma-separated pattern specification
Signed-off-by: Justin Stitt <[email protected]>
1 parent 653584a commit 821ce7a

File tree

11 files changed

+124
-32
lines changed

11 files changed

+124
-32
lines changed

clang/include/clang/AST/Expr.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3860,7 +3860,7 @@ class CStyleCastExpr final
38603860
class BinaryOperator : public Expr {
38613861
enum { LHS, RHS, END_EXPR };
38623862
Stmt *SubExprs[END_EXPR];
3863-
bool isOverflowIdiom;
3863+
bool ExcludedOverflowPattern;
38643864

38653865
public:
38663866
typedef BinaryOperatorKind Opcode;
@@ -4019,7 +4019,7 @@ class BinaryOperator : public Expr {
40194019
return isShiftAssignOp(getOpcode());
40204020
}
40214021

4022-
bool ignoreOverflowSanitizers() const { return isOverflowIdiom; }
4022+
bool ignoreOverflowSanitizers() const { return ExcludedOverflowPattern; }
40234023

40244024
/// Return true if a binary operator using the specified opcode and operands
40254025
/// would match the 'p = (i8*)nullptr + n' idiom for casting a pointer-sized

clang/include/clang/Basic/LangOptions.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,21 @@ class LangOptionsBase {
367367
PerThread,
368368
};
369369

370+
/// Exclude certain code patterns from being instrumented by arithmetic
371+
/// overflow sanitizers
372+
enum OverflowPatternExclusionKind {
373+
/// Don't exclude any overflow patterns from sanitizers
374+
None = 1 << 0,
375+
/// Exclude all overflow patterns (below)
376+
All = 1 << 1,
377+
/// if (a + b < a)
378+
AddOverflowTest = 1 << 2,
379+
/// -1UL
380+
NegUnsignedConst = 1 << 3,
381+
/// while (count--)
382+
PostDecrInWhile = 1 << 4,
383+
};
384+
370385
enum class DefaultVisiblityExportMapping {
371386
None,
372387
/// map only explicit default visibilities to exported
@@ -555,6 +570,11 @@ class LangOptions : public LangOptionsBase {
555570
/// The default stream kind used for HIP kernel launching.
556571
GPUDefaultStreamKind GPUDefaultStream;
557572

573+
/// Which overflow patterns should be excluded from sanitizer instrumentation
574+
int OverflowPatternExclusionMask = 0;
575+
576+
std::vector<std::string> OverflowPatternExclusionValues;
577+
558578
/// The seed used by the randomize structure layout feature.
559579
std::string RandstructSeed;
560580

@@ -630,6 +650,14 @@ class LangOptions : public LangOptionsBase {
630650
return MSCompatibilityVersion >= MajorVersion * 100000U;
631651
}
632652

653+
bool isOverflowPatternExcluded(OverflowPatternExclusionKind Kind) const {
654+
if (OverflowPatternExclusionMask & OverflowPatternExclusionKind::None)
655+
return false;
656+
if (OverflowPatternExclusionMask & OverflowPatternExclusionKind::All)
657+
return true;
658+
return OverflowPatternExclusionMask & Kind;
659+
}
660+
633661
/// Reset all of the options that are not considered when building a
634662
/// module.
635663
void resetNonModularOptions();

clang/include/clang/Driver/Options.td

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2563,11 +2563,11 @@ defm sanitize_overflow_idioms : BoolFOption<"sanitize-overflow-idioms",
25632563
PosFlag<SetTrue, [], [], "Enable">,
25642564
NegFlag<SetFalse, [], [], "Disable">,
25652565
BothFlags<[], [ClangOption], " the instrumentation of common overflow idioms">>;
2566-
defm sanitize_negation_overflow : BoolFOption<"sanitize-negation-overflow",
2567-
LangOpts<"IgnoreNegationOverflow">, DefaultFalse,
2568-
PosFlag<SetFalse, [], [], "Enable">,
2569-
NegFlag<SetTrue, [], [], "Disable">,
2570-
BothFlags<[], [ClangOption], " integer overflow sanitizer instrumentation for negation">>;
2566+
def fsanitize_overflow_pattern_exclusion_EQ : CommaJoined<["-"], "fsanitize-overflow-pattern-exclusion=">,
2567+
HelpText<"Specify the overflow patterns to exclude from artihmetic sanitizer instrumentation">,
2568+
Visibility<[ClangOption, CC1Option]>,
2569+
Values<"none,all,add-overflow-test,negated-unsigned-const,post-decr-while">,
2570+
MarshallingInfoStringVector<LangOpts<"OverflowPatternExclusionValues">>;
25712571
def fsanitize_thread_memory_access : Flag<["-"], "fsanitize-thread-memory-access">,
25722572
Group<f_clang_Group>,
25732573
HelpText<"Enable memory access instrumentation in ThreadSanitizer (default)">;

clang/include/clang/Driver/SanitizerArgs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class SanitizerArgs {
3333
std::vector<std::string> BinaryMetadataIgnorelistFiles;
3434
int CoverageFeatures = 0;
3535
int BinaryMetadataFeatures = 0;
36+
int OverflowPatternExclusions = 0;
3637
int MsanTrackOrigins = 0;
3738
bool MsanUseAfterDtor = true;
3839
bool MsanParamRetval = true;

clang/lib/AST/Expr.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4819,10 +4819,11 @@ BinaryOperator::BinaryOperator(const ASTContext &Ctx, Expr *lhs, Expr *rhs,
48194819
BinaryOperatorBits.OpLoc = opLoc;
48204820
SubExprs[LHS] = lhs;
48214821
SubExprs[RHS] = rhs;
4822-
if (!Ctx.getLangOpts().SanitizeOverflowIdioms) {
4822+
if (Ctx.getLangOpts().isOverflowPatternExcluded(
4823+
LangOptions::OverflowPatternExclusionKind::AddOverflowTest)) {
48234824
std::optional<BinaryOperator *> Result = getOverflowIdiomBinOp(this);
48244825
if (Result.has_value())
4825-
Result.value()->isOverflowIdiom = true;
4826+
Result.value()->ExcludedOverflowPattern = true;
48264827
}
48274828
BinaryOperatorBits.HasFPFeatures = FPFeatures.requiresTrailingStorage();
48284829
if (hasStoredFPFeatures())

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,11 @@ static bool CanElideOverflowCheck(const ASTContext &Ctx, const BinOpInfo &Op) {
198198

199199
const UnaryOperator *UO = dyn_cast<UnaryOperator>(Op.E);
200200

201+
// TODO: rename or rework NegUnsignedConst because it doesn't only work on
202+
// constants.
201203
if (UO && UO->getOpcode() == UO_Minus &&
202-
Ctx.getLangOpts().IgnoreNegationOverflow)
204+
Ctx.getLangOpts().isOverflowPatternExcluded(
205+
LangOptions::OverflowPatternExclusionKind::NegUnsignedConst))
203206
return true;
204207

205208
// If a unary op has a widened operand, the op cannot overflow.
@@ -2888,11 +2891,11 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
28882891
QualType promotedType;
28892892
bool canPerformLossyDemotionCheck = false;
28902893

2891-
// Does this match the pattern of while(i--) {...}? If so, and if
2892-
// SanitizeOverflowIdioms is disabled, we don't want to enable sanitizer.
2894+
// Is the pattern "while (i--)" and overflow exclusion?
28932895
bool disableSanitizer =
28942896
(!isInc && !isPre &&
2895-
!CGF.getContext().getLangOpts().SanitizeOverflowIdioms &&
2897+
CGF.getContext().getLangOpts().isOverflowPatternExcluded(
2898+
LangOptions::OverflowPatternExclusionKind::PostDecrInWhile) &&
28962899
llvm::all_of(CGF.getContext().getParentMapContext().getParents(*E),
28972900
[&](const DynTypedNode &Parent) -> bool {
28982901
return Parent.get<WhileStmt>();

clang/lib/Driver/SanitizerArgs.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ static SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
119119
static int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A,
120120
bool DiagnoseErrors);
121121

122+
static int parseOverflowPatternExclusionValues(const Driver &D,
123+
const llvm::opt::Arg *A,
124+
bool DiagnoseErrors);
125+
122126
/// Parse -f(no-)?sanitize-metadata= flag values, diagnosing any invalid
123127
/// components. Returns OR of members of \c BinaryMetadataFeature enumeration.
124128
static int parseBinaryMetadataFeatures(const Driver &D, const llvm::opt::Arg *A,
@@ -788,6 +792,13 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
788792
<< "fsanitize-trap=cfi";
789793
}
790794

795+
for (const auto *Arg :
796+
Args.filtered(options::OPT_fsanitize_overflow_pattern_exclusion_EQ)) {
797+
Arg->claim();
798+
OverflowPatternExclusions |=
799+
parseOverflowPatternExclusionValues(D, Arg, DiagnoseErrors);
800+
}
801+
791802
// Parse -f(no-)?sanitize-coverage flags if coverage is supported by the
792803
// enabled sanitizers.
793804
for (const auto *Arg : Args) {
@@ -1245,6 +1256,10 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
12451256
addSpecialCaseListOpt(Args, CmdArgs,
12461257
"-fsanitize-system-ignorelist=", SystemIgnorelistFiles);
12471258

1259+
if (OverflowPatternExclusions)
1260+
Args.AddAllArgs(CmdArgs,
1261+
options::OPT_fsanitize_overflow_pattern_exclusion_EQ);
1262+
12481263
if (MsanTrackOrigins)
12491264
CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins=" +
12501265
Twine(MsanTrackOrigins)));
@@ -1433,6 +1448,28 @@ SanitizerMask parseArgValues(const Driver &D, const llvm::opt::Arg *A,
14331448
return Kinds;
14341449
}
14351450

1451+
static int parseOverflowPatternExclusionValues(const Driver &D,
1452+
const llvm::opt::Arg *A,
1453+
bool DiagnoseErrors) {
1454+
int Exclusions = 0;
1455+
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
1456+
const char *Value = A->getValue(i);
1457+
int E =
1458+
llvm::StringSwitch<int>(Value)
1459+
.Case("none", LangOptionsBase::None)
1460+
.Case("all", LangOptionsBase::All)
1461+
.Case("add-overflow-test", LangOptionsBase::AddOverflowTest)
1462+
.Case("negated-unsigned-const", LangOptionsBase::NegUnsignedConst)
1463+
.Case("post-decr-while", LangOptionsBase::PostDecrInWhile)
1464+
.Default(0);
1465+
if (E == 0)
1466+
D.Diag(clang::diag::err_drv_unsupported_option_argument)
1467+
<< A->getSpelling() << Value;
1468+
Exclusions |= E;
1469+
}
1470+
return Exclusions;
1471+
}
1472+
14361473
int parseCoverageFeatures(const Driver &D, const llvm::opt::Arg *A,
14371474
bool DiagnoseErrors) {
14381475
assert(A->getOption().matches(options::OPT_fsanitize_coverage) ||

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6844,10 +6844,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
68446844
}
68456845
}
68466846

6847-
if (Args.hasArg(options::OPT_fno_sanitize_overflow_idioms) &&
6848-
!Args.hasArg(options::OPT_fsanitize_negation_overflow))
6849-
CmdArgs.push_back("-fno-sanitize-negation-overflow");
6850-
68516847
if (Args.getLastArg(options::OPT_fapple_kext) ||
68526848
(Args.hasArg(options::OPT_mkernel) && types::isCXX(InputType)))
68536849
CmdArgs.push_back("-fapple-kext");
@@ -6901,9 +6897,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
69016897
Args.addOptInFlag(CmdArgs, options::OPT_mspeculative_load_hardening,
69026898
options::OPT_mno_speculative_load_hardening);
69036899

6904-
Args.AddLastArg(CmdArgs, options::OPT_fsanitize_negation_overflow,
6905-
options::OPT_fno_sanitize_negation_overflow);
6906-
69076900
RenderSSPOptions(D, TC, Args, CmdArgs, KernelOrKext);
69086901
RenderSCPOptions(TC, Args, CmdArgs);
69096902
RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs);
@@ -7753,6 +7746,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
77537746
Args.AddLastArg(CmdArgs, options::OPT_fgpu_default_stream_EQ);
77547747
}
77557748

7749+
Args.AddAllArgs(CmdArgs,
7750+
options::OPT_fsanitize_overflow_pattern_exclusion_EQ);
7751+
77567752
Args.AddLastArg(CmdArgs, options::OPT_foffload_uniform_block,
77577753
options::OPT_fno_offload_uniform_block);
77587754

clang/lib/Frontend/CompilerInvocation.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4248,6 +4248,22 @@ bool CompilerInvocation::ParseLangArgs(LangOptions &Opts, ArgList &Args,
42484248
Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val;
42494249
}
42504250

4251+
if (auto *A = Args.getLastArg(OPT_fsanitize_overflow_pattern_exclusion_EQ)) {
4252+
for (int i = 0, n = A->getNumValues(); i != n; ++i) {
4253+
StringRef Value = A->getValue(i);
4254+
if (Value == "none")
4255+
Opts.OverflowPatternExclusionMask |= LangOptionsBase::None;
4256+
else if (Value == "all")
4257+
Opts.OverflowPatternExclusionMask |= LangOptionsBase::All;
4258+
else if (Value == "add-overflow-test")
4259+
Opts.OverflowPatternExclusionMask |= LangOptionsBase::AddOverflowTest;
4260+
else if (Value == "negated-unsigned-const")
4261+
Opts.OverflowPatternExclusionMask |= LangOptionsBase::NegUnsignedConst;
4262+
else if (Value == "post-decr-while")
4263+
Opts.OverflowPatternExclusionMask |= LangOptionsBase::PostDecrInWhile;
4264+
}
4265+
}
4266+
42514267
// Parse -fsanitize= arguments.
42524268
parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ),
42534269
Diags, Opts.Sanitize);

clang/test/CodeGen/overflow-idiom-exclusion-fp.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Check for potential false positives from patterns that _almost_ match classic overflow idioms
2-
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fno-sanitize-overflow-idioms -S -emit-llvm -o - | FileCheck %s
3-
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fno-sanitize-overflow-idioms -fwrapv -S -emit-llvm -o - | FileCheck %s
2+
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-overflow-pattern-exclusion=all -S -emit-llvm -o - | FileCheck %s
3+
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-overflow-pattern-exclusion=all -fwrapv -S -emit-llvm -o - | FileCheck %s
44

55
extern unsigned a, b, c;
66
extern int u, v, w;

clang/test/CodeGen/overflow-idiom-exclusion.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,25 @@
1616
// unsigned negation, for example:
1717
// unsigned long A = -1UL;
1818

19-
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fno-sanitize-overflow-idioms -S -emit-llvm -o - | FileCheck %s
20-
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fno-sanitize-overflow-idioms -fwrapv -S -emit-llvm -o - | FileCheck %s
21-
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fno-sanitize-overflow-idioms -S -emit-llvm -o - | FileCheck %s --check-prefix=NEGATION
22-
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fno-sanitize-overflow-idioms -fsanitize-negation-overflow -S -emit-llvm -o - | FileCheck %s --check-prefix=NEGATIONOV
23-
// CHECK-NOT: br{{.*}}overflow
19+
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-overflow-pattern-exclusion=all -S -emit-llvm -o - | FileCheck %s
20+
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-overflow-pattern-exclusion=all -fwrapv -S -emit-llvm -o - | FileCheck %s
21+
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-overflow-pattern-exclusion=add-overflow-test -S -emit-llvm -o - | FileCheck %s --check-prefix=ADD
22+
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-overflow-pattern-exclusion=negated-unsigned-const -S -emit-llvm -o - | FileCheck %s --check-prefix=NEGATE
23+
// RUN: %clang %s -O2 -fsanitize=signed-integer-overflow,unsigned-integer-overflow -fsanitize-overflow-pattern-exclusion=post-decr-while -S -emit-llvm -o - | FileCheck %s --check-prefix=WHILE
2424

25+
// CHECK-NOT: handle{{.*}}overflow
26+
27+
// ADD: usub.with.overflow
28+
// ADD: negate_overflow
29+
// ADD-NOT: handler.add_overflow
30+
31+
// NEGATE: handler.add_overflow
32+
// NEGATE: usub.with.overflow
33+
// NEGATE-NOT: negate_overflow
34+
35+
// WHILE: handler.add_overflow
36+
// WHILE: negate_overflow
37+
// WHILE-NOT: usub.with.overflow
2538
extern unsigned a, b, c;
2639
extern unsigned some(void);
2740

@@ -110,16 +123,13 @@ void common_while(unsigned i) {
110123
}
111124
}
112125

113-
// NEGATION-LABEL,NEGATIONOV-LABEL: define{{.*}}negation_overflow
114-
// NEGATION-NOT: negate_overflow
115-
// NEGATIONOV: negate_overflow
116126
// Normally, these assignments would trip the unsigned overflow sanitizer.
117-
void negation_overflow(void) {
127+
void negation(void) {
118128
#define SOME -1UL
119129
unsigned long A = -1UL;
120130
unsigned long B = -2UL;
121131
unsigned long C = -3UL;
122-
unsigned long D = -1337UL;
132+
unsigned long D = -SOME;
123133
(void)A;(void)B;(void)C;(void)D;
124134
}
125135

0 commit comments

Comments
 (0)