Skip to content

Remove -bounds-checking-unique-traps (replace with -fno-sanitize-merge=local-bounds) #120682

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

Merged
merged 5 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -1208,6 +1202,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
Loading