Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 2 additions & 1 deletion clang/lib/CodeGen/BackendUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 28 additions & 0 deletions compiler-rt/test/ubsan/TestCases/Misc/local_bounds.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RUN: %clangxx -fsanitize=local-bounds %s -O3 -o %t && %run %t 1
// RUN: %clangxx -fsanitize=local-bounds %s -O3 -o %t && not --crash %run %t 3

// FIXME: it's always trap for now.

#include <cstdlib>

struct S {
int k;
int l;
};

__attribute__((noinline)) void init(S *s) {
__asm__ __volatile__("" : : "r"(s) : "memory");
}

__attribute__((noinline, no_sanitize("memory"))) int test(char i) {
S a;
init(&a);
S b;
init(&b);
return ((int*)(&a))[i];
}

int main(int argc, char **argv) {
test(argv[1][0] - '0');
return 0;
}
18 changes: 17 additions & 1 deletion llvm/include/llvm/Transforms/Instrumentation/BoundsChecking.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 6 additions & 1 deletion llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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
Expand Down
29 changes: 27 additions & 2 deletions llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
}
95 changes: 95 additions & 0 deletions llvm/test/Instrumentation/BoundsChecking/runtimes.ll
Original file line number Diff line number Diff line change
@@ -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
}
Loading