[clang] [compiler-rt] [llvm] [ubsan] Add runtime test for -fsanitize=local-bounds (PR #120038)
Vitaly Buka via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 18 15:18:18 PST 2024
https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/120038
>From b42a2ec4a07d94c6c0d73d4baedf2ffef3d3825c Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Sun, 15 Dec 2024 21:24:50 -0800
Subject: [PATCH 1/2] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.4
[skip ci]
---
clang/lib/CodeGen/BackendUtil.cpp | 3 +-
.../Instrumentation/BoundsChecking.h | 18 +++-
llvm/lib/Passes/PassBuilder.cpp | 27 ++++++
llvm/lib/Passes/PassRegistry.def | 7 +-
.../Instrumentation/BoundsChecking.cpp | 29 +++++-
.../BoundsChecking/runtimes.ll | 95 +++++++++++++++++++
6 files changed, 174 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/Instrumentation/BoundsChecking/runtimes.ll
diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp
index 2ef098172c01f5..1e99efd086478f 100644
--- a/clang/lib/CodeGen/BackendUtil.cpp
+++ b/clang/lib/CodeGen/BackendUtil.cpp
@@ -1023,7 +1023,8 @@ void EmitAssemblyHelper::RunOptimizationPipeline(
if (LangOpts.Sanitize.has(SanitizerKind::LocalBounds))
PB.registerScalarOptimizerLateEPCallback(
[](FunctionPassManager &FPM, OptimizationLevel Level) {
- FPM.addPass(BoundsCheckingPass());
+ FPM.addPass(
+ BoundsCheckingPass(BoundsCheckingPass::ReportingMode::Trap));
});
// Don't add sanitizers if we are here from ThinLTO PostLink. That already
diff --git a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
index b1b1ece3eff5a0..1876e5b72e8c99 100644
--- a/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
+++ b/llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
@@ -16,9 +16,25 @@ class Function;
/// A pass to instrument code and perform run-time bounds checking on loads,
/// stores, and other memory intrinsics.
-struct BoundsCheckingPass : PassInfoMixin<BoundsCheckingPass> {
+class BoundsCheckingPass : public PassInfoMixin<BoundsCheckingPass> {
+public:
+ enum class ReportingMode {
+ Trap,
+ MinRuntime,
+ MinRuntimeAbort,
+ FullRuntime,
+ FullRuntimeAbort,
+ };
+
+private:
+ ReportingMode Mode = ReportingMode::Trap;
+
+public:
+ BoundsCheckingPass(ReportingMode Mode) : Mode(Mode) {}
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
static bool isRequired() { return true; }
+ void printPipeline(raw_ostream &OS,
+ function_ref<StringRef(StringRef)> MapClassName2PassName);
};
} // end namespace llvm
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 6e1cefdb3f4d70..6879c101086697 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1281,6 +1281,33 @@ parseRegAllocFastPassOptions(PassBuilder &PB, StringRef Params) {
return Opts;
}
+Expected<BoundsCheckingPass::ReportingMode>
+parseBoundsCheckingOptions(StringRef Params) {
+ BoundsCheckingPass::ReportingMode Mode =
+ BoundsCheckingPass::ReportingMode::Trap;
+ while (!Params.empty()) {
+ StringRef ParamName;
+ std::tie(ParamName, Params) = Params.split(';');
+ if (ParamName == "trap") {
+ Mode = BoundsCheckingPass::ReportingMode::Trap;
+ } else if (ParamName == "rt") {
+ Mode = BoundsCheckingPass::ReportingMode::FullRuntime;
+ } else if (ParamName == "rt-abort") {
+ Mode = BoundsCheckingPass::ReportingMode::FullRuntimeAbort;
+ } else if (ParamName == "min-rt") {
+ Mode = BoundsCheckingPass::ReportingMode::MinRuntime;
+ } else if (ParamName == "min-rt-abort") {
+ Mode = BoundsCheckingPass::ReportingMode::MinRuntimeAbort;
+ } else {
+ return make_error<StringError>(
+ formatv("invalid BoundsChecking pass parameter '{0}' ", ParamName)
+ .str(),
+ inconvertibleErrorCode());
+ }
+ }
+ return Mode;
+}
+
} // namespace
/// Tests whether a pass name starts with a valid prefix for a default pipeline
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index b7fc7d2ac7fee3..a8b0a349af122a 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -339,7 +339,6 @@ FUNCTION_PASS("assume-builder", AssumeBuilderPass())
FUNCTION_PASS("assume-simplify", AssumeSimplifyPass())
FUNCTION_PASS("atomic-expand", AtomicExpandPass(TM))
FUNCTION_PASS("bdce", BDCEPass())
-FUNCTION_PASS("bounds-checking", BoundsCheckingPass())
FUNCTION_PASS("break-crit-edges", BreakCriticalEdgesPass())
FUNCTION_PASS("callbr-prepare", CallBrPreparePass())
FUNCTION_PASS("callsite-splitting", CallSiteSplittingPass())
@@ -620,6 +619,12 @@ FUNCTION_PASS_WITH_PARAMS(
return WinEHPreparePass(DemoteCatchSwitchPHIOnly);
},
parseWinEHPrepareOptions, "demote-catchswitch-only")
+FUNCTION_PASS_WITH_PARAMS(
+ "bounds-checking", "BoundsCheckingPass",
+ [](BoundsCheckingPass::ReportingMode Mode) {
+ return BoundsCheckingPass(Mode);
+ },
+ parseBoundsCheckingOptions, "trap")
#undef FUNCTION_PASS_WITH_PARAMS
#ifndef LOOPNEST_PASS
diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
index b398a13383b9eb..c4511d574f2185 100644
--- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
+++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
@@ -126,16 +126,18 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
BasicBlock *Cont = OldBB->splitBasicBlock(SplitI);
OldBB->getTerminator()->eraseFromParent();
+ BasicBlock * TrapBB = GetTrapBB(IRB);
+
if (C) {
// If we have a constant zero, unconditionally branch.
// FIXME: We should really handle this differently to bypass the splitting
// the block.
- BranchInst::Create(GetTrapBB(IRB), OldBB);
+ BranchInst::Create(TrapBB, OldBB);
return;
}
// Create the conditional branch.
- BranchInst::Create(GetTrapBB(IRB), Cont, Or, OldBB);
+ BranchInst::Create(TrapBB, Cont, Or, OldBB);
}
static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
@@ -229,3 +231,26 @@ PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &
return PreservedAnalyses::none();
}
+
+void BoundsCheckingPass::printPipeline(
+ raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
+ static_cast<PassInfoMixin<BoundsCheckingPass> *>(this)->printPipeline(
+ OS, MapClassName2PassName);
+ switch (Mode) {
+ case ReportingMode::Trap:
+ OS << "<trap>";
+ break;
+ case ReportingMode::MinRuntime:
+ OS << "<min-rt>";
+ break;
+ case ReportingMode::MinRuntimeAbort:
+ OS << "<min-rt-abort>";
+ break;
+ case ReportingMode::FullRuntime:
+ OS << "<rt>";
+ break;
+ case ReportingMode::FullRuntimeAbort:
+ OS << "<rt-abort>";
+ break;
+ }
+}
\ No newline at end of file
diff --git a/llvm/test/Instrumentation/BoundsChecking/runtimes.ll b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
new file mode 100644
index 00000000000000..fd27694c155d2b
--- /dev/null
+++ b/llvm/test/Instrumentation/BoundsChecking/runtimes.ll
@@ -0,0 +1,95 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=bounds-checking -S | FileCheck %s --check-prefixes=TR
+; RUN: opt < %s -passes='bounds-checking<trap>' -S | FileCheck %s --check-prefixes=TR
+; RUN: opt < %s -passes='bounds-checking<rt>' -S | FileCheck %s --check-prefixes=RT
+; RUN: opt < %s -passes='bounds-checking<rt-abort>' -S | FileCheck %s --check-prefixes=RTABORT
+; RUN: opt < %s -passes='bounds-checking<min-rt>' -S | FileCheck %s --check-prefixes=MINRT
+; RUN: opt < %s -passes='bounds-checking<min-rt-abort>' -S | FileCheck %s --check-prefixes=MINRTABORT
+
+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"
+
+define void @f1(i64 %x) nounwind {
+; TR-LABEL: define void @f1(
+; TR-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; TR-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; TR-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; TR-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; TR-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; TR-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; TR-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; TR-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; TR: [[BB7]]:
+; TR-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; TR-NEXT: ret void
+; TR: [[TRAP]]:
+; TR-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
+; TR-NEXT: unreachable
+;
+; RT-LABEL: define void @f1(
+; RT-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; RT-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; RT-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; RT-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; RT-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; RT-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; RT-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; RT-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; RT: [[BB7]]:
+; RT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; RT-NEXT: ret void
+; RT: [[TRAP]]:
+; RT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
+; RT-NEXT: unreachable
+;
+; RTABORT-LABEL: define void @f1(
+; RTABORT-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; RTABORT-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; RTABORT-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; RTABORT-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; RTABORT-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; RTABORT-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; RTABORT-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; RTABORT-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; RTABORT: [[BB7]]:
+; RTABORT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; RTABORT-NEXT: ret void
+; RTABORT: [[TRAP]]:
+; RTABORT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
+; RTABORT-NEXT: unreachable
+;
+; MINRT-LABEL: define void @f1(
+; MINRT-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; MINRT-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; MINRT-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; MINRT-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; MINRT-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; MINRT-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; MINRT-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; MINRT-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; MINRT: [[BB7]]:
+; MINRT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; MINRT-NEXT: ret void
+; MINRT: [[TRAP]]:
+; MINRT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
+; MINRT-NEXT: unreachable
+;
+; MINRTABORT-LABEL: define void @f1(
+; MINRTABORT-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; MINRTABORT-NEXT: [[TMP1:%.*]] = mul i64 16, [[X]]
+; MINRTABORT-NEXT: [[TMP2:%.*]] = alloca i128, i64 [[X]], align 8
+; MINRTABORT-NEXT: [[TMP3:%.*]] = sub i64 [[TMP1]], 0
+; MINRTABORT-NEXT: [[TMP4:%.*]] = icmp ult i64 [[TMP3]], 16
+; MINRTABORT-NEXT: [[TMP5:%.*]] = or i1 false, [[TMP4]]
+; MINRTABORT-NEXT: [[TMP6:%.*]] = or i1 false, [[TMP5]]
+; MINRTABORT-NEXT: br i1 [[TMP6]], label %[[TRAP:.*]], label %[[BB7:.*]]
+; MINRTABORT: [[BB7]]:
+; MINRTABORT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
+; MINRTABORT-NEXT: ret void
+; MINRTABORT: [[TRAP]]:
+; MINRTABORT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
+; MINRTABORT-NEXT: unreachable
+;
+ %1 = alloca i128, i64 %x
+ %3 = load i128, ptr %1, align 4
+ ret void
+}
>From 0c12eaff9f2e4c175268d8f021825b115b2771ec Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Wed, 18 Dec 2024 15:18:04 -0800
Subject: [PATCH 2/2] rebase
Created using spr 1.3.4
---
compiler-rt/test/ubsan/TestCases/Misc/local_bounds.cpp | 2 +-
compiler-rt/test/ubsan_minimal/TestCases/local_bounds.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/compiler-rt/test/ubsan/TestCases/Misc/local_bounds.cpp b/compiler-rt/test/ubsan/TestCases/Misc/local_bounds.cpp
index 92e9e62dbc8698..edfe439c92790d 100644
--- a/compiler-rt/test/ubsan/TestCases/Misc/local_bounds.cpp
+++ b/compiler-rt/test/ubsan/TestCases/Misc/local_bounds.cpp
@@ -19,7 +19,7 @@ __attribute__((noinline, no_sanitize("memory"))) int test(char i) {
init(&a);
S b;
init(&b);
- return ((int*)(&a))[i];
+ return ((int *)(&a))[i];
}
int main(int argc, char **argv) {
diff --git a/compiler-rt/test/ubsan_minimal/TestCases/local_bounds.cpp b/compiler-rt/test/ubsan_minimal/TestCases/local_bounds.cpp
index 92e9e62dbc8698..edfe439c92790d 100644
--- a/compiler-rt/test/ubsan_minimal/TestCases/local_bounds.cpp
+++ b/compiler-rt/test/ubsan_minimal/TestCases/local_bounds.cpp
@@ -19,7 +19,7 @@ __attribute__((noinline, no_sanitize("memory"))) int test(char i) {
init(&a);
S b;
init(&b);
- return ((int*)(&a))[i];
+ return ((int *)(&a))[i];
}
int main(int argc, char **argv) {
More information about the llvm-commits
mailing list