Skip to content

Commit

Permalink
Remove -bounds-checking-unique-traps (replace with -fno-sanitize-merg…
Browse files Browse the repository at this point in the history
…e=local-bounds) (llvm#120682)

llvm#120613 removed -ubsan-unique-traps and replaced it with
-fno-sanitize-merge (introduced in llvm#120511), which allows fine-grained
control of which UBSan checks to prevent merging. This analogous patch
removes -bound-checking-unique-traps, and allows it to be controlled via
-fno-sanitize-merge=local-bounds.

Most of this patch is simply plumbing through the compiler flags into
the bounds checking pass.

Note: this patch subtly changes -fsanitize-merge (the default) to also
include -fsanitize-merge=local-bounds. This is different from the
previous behavior, where -fsanitize-merge (or the old
-ubsan-unique-traps) did not affect local-bounds (requiring the separate
-bounds-checking-unique-traps). However, we argue that the new behavior
is more intuitive.

Removing -bounds-checking-unique-traps and merging its functionality
into -fsanitize-merge breaks backwards compatibility; we hope that this
is acceptable since '-mllvm -bounds-checking-unique-traps' was an
experimental flag.
  • Loading branch information
thurstond authored Dec 20, 2024
1 parent 82b5bda commit 5bb6503
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 102 deletions.
11 changes: 5 additions & 6 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -445,10 +445,6 @@ New Compiler Flags
- The ``-Warray-compare-cxx26`` warning has been added to warn about array comparison
starting from C++26, this warning is enabled as an error by default.

- '-fsanitize-merge' (default) and '-fno-sanitize-merge' have been added for
fine-grained control of which UBSan checks are allowed to be merged by the
backend (for example, -fno-sanitize-merge=bool,enum).

Deprecated Compiler Flags
-------------------------

Expand Down Expand Up @@ -488,8 +484,6 @@ Removed Compiler Flags
derivatives) is now removed, since it's no longer possible to suppress the
diagnostic (see above). Users can expect an `unknown warning` diagnostic if
it's still in use.
- The experimental flag '-ubsan-unique-traps' has been removed. It is
superseded by '-fno-sanitize-merge'.

Attribute Changes in Clang
--------------------------
Expand Down Expand Up @@ -1213,6 +1207,11 @@ Sanitizers

- Implemented ``-f[no-]sanitize-trap=local-bounds``, and ``-f[no-]sanitize-recover=local-bounds``.

- ``-fsanitize-merge`` (default) and ``-fno-sanitize-merge`` have been added for
fine-grained, unified control of which UBSan checks can potentially be merged
by the compiler (for example,
``-fno-sanitize-merge=bool,enum,array-bounds,local-bounds``).

Python Binding Changes
----------------------
- Fixed an issue that led to crashes when calling ``Type.get_exception_specification_kind``.
Expand Down
4 changes: 2 additions & 2 deletions clang/docs/UndefinedBehaviorSanitizer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,8 @@ Stack traces and report symbolization
If you want UBSan to print symbolized stack trace for each error report, you
will need to:

#. Compile with ``-g`` and ``-fno-omit-frame-pointer`` to get proper debug
information in your binary.
#. Compile with ``-g``, ``-fno-sanitize-merge`` and ``-fno-omit-frame-pointer``
to get proper debug information in your binary.
#. Run your program with environment variable
``UBSAN_OPTIONS=print_stacktrace=1``.
#. Make sure ``llvm-symbolizer`` binary is in ``PATH``.
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,9 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
PB.registerScalarOptimizerLateEPCallback(
[this](FunctionPassManager &FPM, OptimizationLevel Level) {
BoundsCheckingPass::ReportingMode Mode;
bool Merge = CodeGenOpts.SanitizeMergeHandlers.has(
SanitizerKind::LocalBounds);

if (CodeGenOpts.SanitizeTrap.has(SanitizerKind::LocalBounds)) {
Mode = BoundsCheckingPass::ReportingMode::Trap;
} else if (CodeGenOpts.SanitizeMinimalRuntime) {
Expand All @@ -1041,7 +1044,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
? BoundsCheckingPass::ReportingMode::FullRuntime
: BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
}
FPM.addPass(BoundsCheckingPass(Mode));
BoundsCheckingPass::BoundsCheckingOptions Options(Mode, Merge);
FPM.addPass(BoundsCheckingPass(Options));
});

// Don't add sanitizers if we are here from ThinLTO PostLink. That already
Expand Down
14 changes: 8 additions & 6 deletions clang/test/CodeGen/bounds-checking.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s
// RUN: %clang_cc1 -fsanitize=array-bounds -O -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s
// N.B. The clang driver defaults to -fsanitize-merge but clang_cc1 effectively
// defaults to -fno-sanitize-merge.
// RUN: %clang_cc1 -fsanitize=array-bounds -O -fsanitize-trap=array-bounds -emit-llvm -triple x86_64-apple-darwin10 -DNO_DYNAMIC %s -o - | FileCheck %s
// RUN: %clang_cc1 -fsanitize=array-bounds -O -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s
//
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -O3 -mllvm -bounds-checking-unique-traps -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTLOCAL
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s --check-prefixes=NOOPTLOCAL
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s
//
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTLOCAL
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -fno-sanitize-merge -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTLOCAL
// RUN: %clang_cc1 -fsanitize=local-bounds -fsanitize-trap=local-bounds -fsanitize-merge=local-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s --check-prefixes=NOOPTLOCAL
//
// N.B. The clang driver defaults to -fsanitize-merge but clang_cc1 effectively
// defaults to -fno-sanitize-merge.
// RUN: %clang_cc1 -fsanitize=array-bounds -fsanitize-trap=array-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTARRAY
// RUN: %clang_cc1 -fsanitize=array-bounds -fsanitize-trap=array-bounds -fno-sanitize-merge -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | FileCheck %s --check-prefixes=NOOPTARRAY
// RUN: %clang_cc1 -fsanitize=array-bounds -fsanitize-trap=array-bounds -fsanitize-merge=array-bounds -O3 -emit-llvm -triple x86_64-apple-darwin10 %s -o - | not FileCheck %s --check-prefixes=NOOPTARRAY
Expand Down
15 changes: 11 additions & 4 deletions llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Function;
/// A pass to instrument code and perform run-time bounds checking on loads,
/// stores, and other memory intrinsics.
class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {

public:
enum class ReportingMode {
Trap,
Expand All @@ -26,15 +27,21 @@ class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {
FullRuntimeAbort,
};

private:
ReportingMode Mode = ReportingMode::Trap;
struct BoundsCheckingOptions {
BoundsCheckingOptions(ReportingMode Mode, bool Merge);

public:
BoundsCheckingPass(ReportingMode Mode) : Mode(Mode) {}
ReportingMode Mode;
bool Merge;
};

BoundsCheckingPass(BoundsCheckingOptions Options) : Options(Options) {}
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
static bool isRequired() { return true; }
void printPipeline(raw_ostream &OS,
function_ref<StringRef(StringRef)> MapClassName2PassName);

private:
BoundsCheckingOptions Options;
};

} // end namespace llvm
Expand Down
20 changes: 11 additions & 9 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1281,31 +1281,33 @@ parseRegAllocFastPassOptions(PassBuilder &PB, StringRef Params) {
return Opts;
}

Expected<BoundsCheckingPass::ReportingMode>
Expected<BoundsCheckingPass::BoundsCheckingOptions>
parseBoundsCheckingOptions(StringRef Params) {
BoundsCheckingPass::ReportingMode Mode =
BoundsCheckingPass::ReportingMode::Trap;
BoundsCheckingPass::BoundsCheckingOptions Options(
BoundsCheckingPass::ReportingMode::Trap, false);
while (!Params.empty()) {
StringRef ParamName;
std::tie(ParamName, Params) = Params.split(';');
if (ParamName == "trap") {
Mode = BoundsCheckingPass::ReportingMode::Trap;
Options.Mode = BoundsCheckingPass::ReportingMode::Trap;
} else if (ParamName == "rt") {
Mode = BoundsCheckingPass::ReportingMode::FullRuntime;
Options.Mode = BoundsCheckingPass::ReportingMode::FullRuntime;
} else if (ParamName == "rt-abort") {
Mode = BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
Options.Mode = BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
} else if (ParamName == "min-rt") {
Mode = BoundsCheckingPass::ReportingMode::MinRuntime;
Options.Mode = BoundsCheckingPass::ReportingMode::MinRuntime;
} else if (ParamName == "min-rt-abort") {
Mode = BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
Options.Mode = BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
} else if (ParamName == "merge") {
Options.Merge = true;
} else {
return make_error<StringError>(
formatv("invalid BoundsChecking pass parameter '{0}' ", ParamName)
.str(),
inconvertibleErrorCode());
}
}
return Mode;
return Options;
}

} // namespace
Expand Down
4 changes: 2 additions & 2 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -624,8 +624,8 @@ FUNCTION_PASS_WITH_PARAMS(
parseWinEHPrepareOptions, "demote-catchswitch-only")
FUNCTION_PASS_WITH_PARAMS(
"bounds-checking", "BoundsCheckingPass",
[](BoundsCheckingPass::ReportingMode Mode) {
return BoundsCheckingPass(Mode);
[](BoundsCheckingPass::BoundsCheckingOptions Options) {
return BoundsCheckingPass(Options);
},
parseBoundsCheckingOptions, "trap")
#undef FUNCTION_PASS_WITH_PARAMS
Expand Down
39 changes: 23 additions & 16 deletions llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@ using namespace llvm;
static cl::opt<bool> SingleTrapBB("bounds-checking-single-trap",
cl::desc("Use one trap block per function"));

static cl::opt<bool> DebugTrapBB("bounds-checking-unique-traps",
cl::desc("Always use one trap per check"));

STATISTIC(ChecksAdded, "Bounds checks added");
STATISTIC(ChecksSkipped, "Bounds checks skipped");
STATISTIC(ChecksUnable, "Bounds checks unable to add");

using BuilderTy = IRBuilder<TargetFolder>;

BoundsCheckingPass::BoundsCheckingOptions::BoundsCheckingOptions(
ReportingMode Mode, bool Merge)
: Mode(Mode), Merge(Merge) {}

/// Gets the conditions under which memory accessing instructions will overflow.
///
/// \p Ptr is the pointer that will be read/written, and \p InstVal is either
Expand Down Expand Up @@ -105,7 +106,7 @@ static Value *getBoundsCheckCond(Value *Ptr, Value *InstVal,
return Or;
}

static CallInst *InsertTrap(BuilderTy &IRB) {
static CallInst *InsertTrap(BuilderTy &IRB, bool DebugTrapBB) {
if (!DebugTrapBB)
return IRB.CreateIntrinsic(Intrinsic::trap, {}, {});
// FIXME: Ideally we would use the SanitizerHandler::OutOfBounds constant.
Expand Down Expand Up @@ -169,9 +170,10 @@ struct ReportingOpts {
bool MayReturn = false;
bool UseTrap = false;
bool MinRuntime = false;
bool MayMerge = true;
StringRef Name;

ReportingOpts(BoundsCheckingPass::ReportingMode Mode) {
ReportingOpts(BoundsCheckingPass::ReportingMode Mode, bool Merge) {
switch (Mode) {
case BoundsCheckingPass::ReportingMode::Trap:
UseTrap = true;
Expand All @@ -193,6 +195,8 @@ struct ReportingOpts {
Name = "__ubsan_handle_local_out_of_bounds_abort";
break;
}

MayMerge = Merge;
}
};

Expand Down Expand Up @@ -253,13 +257,12 @@ static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
BasicBlock *TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
IRB.SetInsertPoint(TrapBB);

bool DebugTrapBB = !Opts.MayMerge;
CallInst *TrapCall = Opts.UseTrap
? InsertTrap(IRB)
? InsertTrap(IRB, DebugTrapBB)
: InsertCall(IRB, Opts.MayReturn, Opts.Name);
if (DebugTrapBB) {
// FIXME: Pass option form clang.
if (DebugTrapBB)
TrapCall->addFnAttr(llvm::Attribute::NoMerge);
}

TrapCall->setDoesNotThrow();
TrapCall->setDebugLoc(DebugLoc);
Expand Down Expand Up @@ -289,7 +292,8 @@ PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &
auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);

if (!addBoundsChecking(F, TLI, SE, ReportingOpts(Mode)))
if (!addBoundsChecking(F, TLI, SE,
ReportingOpts(Options.Mode, Options.Merge)))
return PreservedAnalyses::all();

return PreservedAnalyses::none();
Expand All @@ -299,21 +303,24 @@ void BoundsCheckingPass::printPipeline(
raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
static_cast<PassInfoMixin<BoundsCheckingPass> *>(this)->printPipeline(
OS, MapClassName2PassName);
switch (Mode) {
switch (Options.Mode) {
case ReportingMode::Trap:
OS << "<trap>";
OS << "<trap";
break;
case ReportingMode::MinRuntime:
OS << "<min-rt>";
OS << "<min-rt";
break;
case ReportingMode::MinRuntimeAbort:
OS << "<min-rt-abort>";
OS << "<min-rt-abort";
break;
case ReportingMode::FullRuntime:
OS << "<rt>";
OS << "<rt";
break;
case ReportingMode::FullRuntimeAbort:
OS << "<rt-abort>";
OS << "<rt-abort";
break;
}
if (Options.Merge)
OS << ";merge";
OS << ">";
}
4 changes: 2 additions & 2 deletions llvm/test/Instrumentation/BoundsChecking/many-trap.ll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
; RUN: opt < %s -passes=bounds-checking -S | FileCheck %s
; RUN: opt < %s -passes=bounds-checking -bounds-checking-single-trap -S | FileCheck -check-prefix=SINGLE %s
; RUN: opt < %s -passes='bounds-checking<merge>' -S | FileCheck %s
; RUN: opt < %s -passes='bounds-checking<merge>' -bounds-checking-single-trap -S | FileCheck -check-prefix=SINGLE %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"

; CHECK: @f1
Expand Down
Loading

0 comments on commit 5bb6503

Please sign in to comment.