Skip to content

Commit 3d29751

Browse files
authored
[BoundsChecking] Add support for runtime handlers (#120513)
This is a step forward to have reporting consistent with other UBSAN checks. Runtime and clang parts are here #120515.
1 parent bcd32ef commit 3d29751

File tree

2 files changed

+89
-26
lines changed

2 files changed

+89
-26
lines changed

llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp

Lines changed: 83 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/Transforms/Instrumentation/BoundsChecking.h"
1010
#include "llvm/ADT/Statistic.h"
11+
#include "llvm/ADT/StringRef.h"
1112
#include "llvm/ADT/Twine.h"
1213
#include "llvm/Analysis/MemoryBuiltins.h"
1314
#include "llvm/Analysis/ScalarEvolution.h"
@@ -104,6 +105,30 @@ static Value *getBoundsCheckCond(Value *Ptr, Value *InstVal,
104105
return Or;
105106
}
106107

108+
static CallInst *InsertTrap(BuilderTy &IRB) {
109+
if (!DebugTrapBB)
110+
return IRB.CreateIntrinsic(Intrinsic::trap, {}, {});
111+
// FIXME: Ideally we would use the SanitizerHandler::OutOfBounds constant.
112+
return IRB.CreateIntrinsic(
113+
Intrinsic::ubsantrap, {},
114+
ConstantInt::get(IRB.getInt8Ty(),
115+
IRB.GetInsertBlock()->getParent()->size()));
116+
}
117+
118+
static CallInst *InsertCall(BuilderTy &IRB, bool MayReturn, StringRef Name) {
119+
Function *Fn = IRB.GetInsertBlock()->getParent();
120+
LLVMContext &Ctx = Fn->getContext();
121+
llvm::AttrBuilder B(Ctx);
122+
B.addAttribute(llvm::Attribute::NoUnwind);
123+
if (!MayReturn)
124+
B.addAttribute(llvm::Attribute::NoReturn);
125+
FunctionCallee Callee = Fn->getParent()->getOrInsertFunction(
126+
Name,
127+
llvm::AttributeList::get(Ctx, llvm::AttributeList::FunctionIndex, B),
128+
Type::getVoidTy(Ctx));
129+
return IRB.CreateCall(Callee);
130+
}
131+
107132
/// Adds run-time bounds checks to memory accessing instructions.
108133
///
109134
/// \p Or is the condition that should guard the trap.
@@ -126,20 +151,53 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
126151
BasicBlock *Cont = OldBB->splitBasicBlock(SplitI);
127152
OldBB->getTerminator()->eraseFromParent();
128153

154+
BasicBlock *TrapBB = GetTrapBB(IRB, Cont);
155+
129156
if (C) {
130157
// If we have a constant zero, unconditionally branch.
131158
// FIXME: We should really handle this differently to bypass the splitting
132159
// the block.
133-
BranchInst::Create(GetTrapBB(IRB), OldBB);
160+
BranchInst::Create(TrapBB, OldBB);
134161
return;
135162
}
136163

137164
// Create the conditional branch.
138-
BranchInst::Create(GetTrapBB(IRB), Cont, Or, OldBB);
165+
BranchInst::Create(TrapBB, Cont, Or, OldBB);
139166
}
140167

168+
struct ReportingOpts {
169+
bool MayReturn = false;
170+
bool UseTrap = false;
171+
bool MinRuntime = false;
172+
StringRef Name;
173+
174+
ReportingOpts(BoundsCheckingPass::ReportingMode Mode) {
175+
switch (Mode) {
176+
case BoundsCheckingPass::ReportingMode::Trap:
177+
UseTrap = true;
178+
break;
179+
case BoundsCheckingPass::ReportingMode::MinRuntime:
180+
Name = "__ubsan_handle_local_out_of_bounds_minimal";
181+
MinRuntime = true;
182+
MayReturn = true;
183+
break;
184+
case BoundsCheckingPass::ReportingMode::MinRuntimeAbort:
185+
Name = "__ubsan_handle_local_out_of_bounds_minimal_abort";
186+
MinRuntime = true;
187+
break;
188+
case BoundsCheckingPass::ReportingMode::FullRuntime:
189+
Name = "__ubsan_handle_local_out_of_bounds";
190+
MayReturn = true;
191+
break;
192+
case BoundsCheckingPass::ReportingMode::FullRuntimeAbort:
193+
Name = "__ubsan_handle_local_out_of_bounds_abort";
194+
break;
195+
}
196+
}
197+
};
198+
141199
static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
142-
ScalarEvolution &SE) {
200+
ScalarEvolution &SE, const ReportingOpts &Opts) {
143201
if (F.hasFnAttribute(Attribute::NoSanitizeBounds))
144202
return false;
145203

@@ -180,39 +238,44 @@ static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
180238
// Create a trapping basic block on demand using a callback. Depending on
181239
// flags, this will either create a single block for the entire function or
182240
// will create a fresh block every time it is called.
183-
BasicBlock *TrapBB = nullptr;
184-
auto GetTrapBB = [&TrapBB](BuilderTy &IRB) {
241+
BasicBlock *ReuseTrapBB = nullptr;
242+
auto GetTrapBB = [&ReuseTrapBB, &Opts](BuilderTy &IRB, BasicBlock *Cont) {
185243
Function *Fn = IRB.GetInsertBlock()->getParent();
186244
auto DebugLoc = IRB.getCurrentDebugLocation();
187245
IRBuilder<>::InsertPointGuard Guard(IRB);
188246

189-
if (TrapBB && SingleTrapBB && !DebugTrapBB)
190-
return TrapBB;
247+
// Create a trapping basic block on demand using a callback. Depending on
248+
// flags, this will either create a single block for the entire function or
249+
// will create a fresh block every time it is called.
250+
if (ReuseTrapBB)
251+
return ReuseTrapBB;
191252

192-
TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
253+
BasicBlock *TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
193254
IRB.SetInsertPoint(TrapBB);
194255

195-
Intrinsic::ID IntrID = DebugTrapBB ? Intrinsic::ubsantrap : Intrinsic::trap;
196-
197-
CallInst *TrapCall;
256+
CallInst *TrapCall = Opts.UseTrap
257+
? InsertTrap(IRB)
258+
: InsertCall(IRB, Opts.MayReturn, Opts.Name);
198259
if (DebugTrapBB) {
199-
// Ideally we would use the SanitizerHandler::OutOfBounds constant
200-
TrapCall = IRB.CreateIntrinsic(
201-
IntrID, {}, ConstantInt::get(IRB.getInt8Ty(), Fn->size()));
260+
// FIXME: Pass option form clang.
202261
TrapCall->addFnAttr(llvm::Attribute::NoMerge);
203-
} else {
204-
TrapCall = IRB.CreateIntrinsic(IntrID, {}, {});
205262
}
206263

207-
TrapCall->setDoesNotReturn();
208264
TrapCall->setDoesNotThrow();
209265
TrapCall->setDebugLoc(DebugLoc);
210-
IRB.CreateUnreachable();
266+
if (Opts.MayReturn) {
267+
IRB.CreateBr(Cont);
268+
} else {
269+
TrapCall->setDoesNotReturn();
270+
IRB.CreateUnreachable();
271+
}
272+
273+
if (!Opts.MayReturn && SingleTrapBB && !DebugTrapBB)
274+
ReuseTrapBB = TrapBB;
211275

212276
return TrapBB;
213277
};
214278

215-
// Add the checks.
216279
for (const auto &Entry : TrapInfo) {
217280
Instruction *Inst = Entry.first;
218281
BuilderTy IRB(Inst->getParent(), BasicBlock::iterator(Inst), TargetFolder(DL));
@@ -226,7 +289,7 @@ PreservedAnalyses BoundsCheckingPass::run(Function &F, FunctionAnalysisManager &
226289
auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
227290
auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
228291

229-
if (!addBoundsChecking(F, TLI, SE))
292+
if (!addBoundsChecking(F, TLI, SE, ReportingOpts(Mode)))
230293
return PreservedAnalyses::all();
231294

232295
return PreservedAnalyses::none();

llvm/test/Instrumentation/BoundsChecking/runtimes.ll

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ define void @f1(i64 %x) nounwind {
3838
; RT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
3939
; RT-NEXT: ret void
4040
; RT: [[TRAP]]:
41-
; RT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
42-
; RT-NEXT: unreachable
41+
; RT-NEXT: call void @__ubsan_handle_local_out_of_bounds() #[[ATTR0]]
42+
; RT-NEXT: br label %[[BB7]]
4343
;
4444
; RTABORT-LABEL: define void @f1(
4545
; RTABORT-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
@@ -54,7 +54,7 @@ define void @f1(i64 %x) nounwind {
5454
; RTABORT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
5555
; RTABORT-NEXT: ret void
5656
; RTABORT: [[TRAP]]:
57-
; RTABORT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
57+
; RTABORT-NEXT: call void @__ubsan_handle_local_out_of_bounds_abort() #[[ATTR1:[0-9]+]]
5858
; RTABORT-NEXT: unreachable
5959
;
6060
; MINRT-LABEL: define void @f1(
@@ -70,8 +70,8 @@ define void @f1(i64 %x) nounwind {
7070
; MINRT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
7171
; MINRT-NEXT: ret void
7272
; MINRT: [[TRAP]]:
73-
; MINRT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
74-
; MINRT-NEXT: unreachable
73+
; MINRT-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal() #[[ATTR0]]
74+
; MINRT-NEXT: br label %[[BB7]]
7575
;
7676
; MINRTABORT-LABEL: define void @f1(
7777
; MINRTABORT-SAME: i64 [[X:%.*]]) #[[ATTR0:[0-9]+]] {
@@ -86,7 +86,7 @@ define void @f1(i64 %x) nounwind {
8686
; MINRTABORT-NEXT: [[TMP8:%.*]] = load i128, ptr [[TMP2]], align 4
8787
; MINRTABORT-NEXT: ret void
8888
; MINRTABORT: [[TRAP]]:
89-
; MINRTABORT-NEXT: call void @llvm.trap() #[[ATTR2:[0-9]+]]
89+
; MINRTABORT-NEXT: call void @__ubsan_handle_local_out_of_bounds_minimal_abort() #[[ATTR1:[0-9]+]]
9090
; MINRTABORT-NEXT: unreachable
9191
;
9292
%1 = alloca i128, i64 %x

0 commit comments

Comments
 (0)