LLVM 24.0.0git
BoundsChecking.cpp
Go to the documentation of this file.
1//===- BoundsChecking.cpp - Instrumentation for run-time bounds checking --===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
11#include "llvm/ADT/Statistic.h"
12#include "llvm/ADT/StringRef.h"
13#include "llvm/ADT/Twine.h"
18#include "llvm/IR/BasicBlock.h"
19#include "llvm/IR/Constants.h"
20#include "llvm/IR/DataLayout.h"
21#include "llvm/IR/Function.h"
22#include "llvm/IR/IRBuilder.h"
24#include "llvm/IR/Instruction.h"
26#include "llvm/IR/Intrinsics.h"
27#include "llvm/IR/Value.h"
29#include "llvm/Support/Debug.h"
31#include <utility>
32
33using namespace llvm;
34
35#define DEBUG_TYPE "bounds-checking"
36
37STATISTIC(ChecksAdded, "Bounds checks added");
38STATISTIC(ChecksSkipped, "Bounds checks skipped");
39STATISTIC(ChecksUnable, "Bounds checks unable to add");
40
42 mutable MDNode *NoSanitizeMD = nullptr;
43
44public:
45 NoSanitizeInserter() = default;
46
47 void InsertHelper(Instruction *I, const Twine &Name,
48 BasicBlock::iterator InsertPt) const {
50 if (!NoSanitizeMD)
51 NoSanitizeMD = MDNode::get(I->getContext(), {});
52 I->setMetadata(LLVMContext::MD_nosanitize, NoSanitizeMD);
53 }
54};
55
57
58/// Gets the conditions under which memory accessing instructions will overflow.
59///
60/// \p Ptr is the pointer that will be read/written, and \p InstVal is either
61/// the result from the load or the value being stored. It is used to determine
62/// the size of memory block that is touched.
63///
64/// Returns the condition under which the access will overflow.
65static Value *getBoundsCheckCond(Value *Ptr, Value *InstVal,
66 const DataLayout &DL, TargetLibraryInfo &TLI,
67 ObjectSizeOffsetEvaluator &ObjSizeEval,
68 BuilderTy &IRB, ScalarEvolution &SE) {
69 TypeSize NeededSize = DL.getTypeStoreSize(InstVal->getType());
70 LLVM_DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize)
71 << " bytes\n");
72
73 SizeOffsetValue SizeOffset = ObjSizeEval.compute(Ptr);
74
75 if (!SizeOffset.bothKnown()) {
76 ++ChecksUnable;
77 return nullptr;
78 }
79
80 Value *Size = SizeOffset.Size;
81 Value *Offset = SizeOffset.Offset;
83
84 Type *IndexTy = DL.getIndexType(Ptr->getType());
85 Value *NeededSizeVal = IRB.CreateTypeSize(IndexTy, NeededSize);
86
87 auto SizeRange = SE.getUnsignedRange(SE.getSCEV(Size));
88 auto OffsetRange = SE.getUnsignedRange(SE.getSCEV(Offset));
89 auto NeededSizeRange = SE.getUnsignedRange(SE.getSCEV(NeededSizeVal));
90
91 // three checks are required to ensure safety:
92 // . Offset >= 0 (since the offset is given from the base ptr)
93 // . Size >= Offset (unsigned)
94 // . Size - Offset >= NeededSize (unsigned)
95 //
96 // optimization: if Size >= 0 (signed), skip 1st check
97 // FIXME: add NSW/NUW here? -- we dont care if the subtraction overflows
98 Value *ObjSize = IRB.CreateSub(Size, Offset);
99 Value *Cmp2 = SizeRange.getUnsignedMin().uge(OffsetRange.getUnsignedMax())
101 : IRB.CreateICmpULT(Size, Offset);
102 Value *Cmp3 = SizeRange.sub(OffsetRange)
103 .getUnsignedMin()
104 .uge(NeededSizeRange.getUnsignedMax())
106 : IRB.CreateICmpULT(ObjSize, NeededSizeVal);
107 Value *Or = IRB.CreateOr(Cmp2, Cmp3);
108 if ((!SizeCI || SizeCI->getValue().slt(0)) &&
109 !SizeRange.getSignedMin().isNonNegative()) {
110 Value *Cmp1 = IRB.CreateICmpSLT(Offset, ConstantInt::get(IndexTy, 0));
111 Or = IRB.CreateOr(Cmp1, Or);
112 }
113
114 return Or;
115}
116
117static CallInst *InsertTrap(BuilderTy &IRB, bool DebugTrapBB,
118 std::optional<int8_t> GuardKind) {
119 if (!DebugTrapBB)
120 return IRB.CreateIntrinsicWithoutFolding(Intrinsic::trap, {});
121
122 uint64_t ImmArg = GuardKind.has_value()
123 ? GuardKind.value()
124 : IRB.GetInsertBlock()->getParent()->size();
125 // Ensure we constrain ImmArg to fitting within a 8-but unsigned integer to
126 // prevent overflow.
127 if (ImmArg > 255)
128 ImmArg = 255;
129
131 Intrinsic::ubsantrap, ConstantInt::get(IRB.getInt8Ty(), ImmArg));
132}
133
134static CallInst *InsertCall(BuilderTy &IRB, bool MayReturn, StringRef Name) {
135 Function *Fn = IRB.GetInsertBlock()->getParent();
136 LLVMContext &Ctx = Fn->getContext();
137 llvm::AttrBuilder B(Ctx);
138 B.addAttribute(llvm::Attribute::NoUnwind);
139 if (!MayReturn)
140 B.addAttribute(llvm::Attribute::NoReturn);
142 Name,
143 llvm::AttributeList::get(Ctx, llvm::AttributeList::FunctionIndex, B),
144 Type::getVoidTy(Ctx));
145 return IRB.CreateCall(Callee);
146}
147
148/// Adds run-time bounds checks to memory accessing instructions.
149///
150/// \p Or is the condition that should guard the trap.
151///
152/// \p GetTrapBB is a callable that returns the trap BB to use on failure.
153template <typename GetTrapBBT>
154static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
155 // check if the comparison is always false
157 if (C) {
158 ++ChecksSkipped;
159 // If non-zero, nothing to do.
160 if (!C->getZExtValue())
161 return;
162 }
163 ++ChecksAdded;
164
166 BasicBlock *OldBB = SplitI->getParent();
167 BasicBlock *Cont = OldBB->splitBasicBlock(SplitI);
168 OldBB->getTerminator()->eraseFromParent();
169
170 BasicBlock *TrapBB = GetTrapBB(IRB, Cont);
171
172 if (C) {
173 // If we have a constant zero, unconditionally branch.
174 // FIXME: We should really handle this differently to bypass the splitting
175 // the block.
176 UncondBrInst::Create(TrapBB, OldBB);
177 return;
178 }
179
180 // Create the conditional branch.
181 CondBrInst::Create(Or, TrapBB, Cont, OldBB);
182}
183
184static std::string
186 std::string Name = "__ubsan_handle_local_out_of_bounds";
187 if (Opts.MinRuntime)
188 Name += "_minimal";
189 if (!Opts.MayReturn)
190 Name += "_abort";
191 else if (Opts.HandlerPreserveAllRegs)
192 Name += "_preserve";
193 return Name;
194}
195
197 ScalarEvolution &SE,
198 const BoundsCheckingPass::Options &Opts,
199 bool SingleTrap) {
200 if (F.hasFnAttribute(Attribute::NoSanitizeBounds))
201 return false;
202
203 const DataLayout &DL = F.getDataLayout();
204 ObjectSizeOpts EvalOpts;
205 EvalOpts.RoundToAlign = true;
207 ObjectSizeOffsetEvaluator ObjSizeEval(*F.getParent(), &TLI, EvalOpts);
208
209 // check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory
210 // touching instructions
212 for (Instruction &I : instructions(F)) {
213 Value *Or = nullptr;
214 BuilderTy IRB(I.getIterator(), TargetFolder(DL));
215 if (LoadInst *LI = dyn_cast<LoadInst>(&I)) {
216 if (!LI->isVolatile())
217 Or = getBoundsCheckCond(LI->getPointerOperand(), LI, DL, TLI,
218 ObjSizeEval, IRB, SE);
219 } else if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {
220 if (!SI->isVolatile())
221 Or = getBoundsCheckCond(SI->getPointerOperand(), SI->getValueOperand(),
222 DL, TLI, ObjSizeEval, IRB, SE);
224 if (!AI->isVolatile())
225 Or =
226 getBoundsCheckCond(AI->getPointerOperand(), AI->getCompareOperand(),
227 DL, TLI, ObjSizeEval, IRB, SE);
228 } else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(&I)) {
229 if (!AI->isVolatile())
230 Or = getBoundsCheckCond(AI->getPointerOperand(), AI->getValOperand(),
231 DL, TLI, ObjSizeEval, IRB, SE);
232 }
233 if (Or) {
234 if (Opts.GuardKind) {
235 llvm::Value *Allow = IRB.CreateIntrinsic(
236 IRB.getInt1Ty(), Intrinsic::allow_ubsan_check,
237 {llvm::ConstantInt::getSigned(IRB.getInt8Ty(), *Opts.GuardKind)});
238 Or = IRB.CreateAnd(Or, Allow);
239 }
240 TrapInfo.push_back(std::make_pair(&I, Or));
241 }
242 }
243
244 std::string Name;
245 if (Opts.Rt)
246 Name = getRuntimeCallName(*Opts.Rt);
247
248 // Create a trapping basic block on demand using a callback. Depending on
249 // flags, this will either create a single block for the entire function or
250 // will create a fresh block every time it is called.
251 BasicBlock *ReuseTrapBB = nullptr;
252 auto GetTrapBB = [&ReuseTrapBB, &Opts, &Name, SingleTrap](BuilderTy &IRB,
253 BasicBlock *Cont) {
254 Function *Fn = IRB.GetInsertBlock()->getParent();
255 auto DebugLoc = IRB.getCurrentDebugLocation();
257
258 // Create a trapping basic block on demand using a callback. Depending on
259 // flags, this will either create a single block for the entire function or
260 // will create a fresh block every time it is called.
261 if (ReuseTrapBB)
262 return ReuseTrapBB;
263
264 BasicBlock *TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
265 IRB.SetInsertPoint(TrapBB);
266
267 bool DebugTrapBB = !Opts.Merge;
268 CallInst *TrapCall = Opts.Rt ? InsertCall(IRB, Opts.Rt->MayReturn, Name)
269 : InsertTrap(IRB, DebugTrapBB, Opts.GuardKind);
270 if (DebugTrapBB)
271 TrapCall->addFnAttr(llvm::Attribute::NoMerge);
272
273 TrapCall->setDoesNotThrow();
274 TrapCall->setDebugLoc(DebugLoc);
275
276 bool MayReturn = Opts.Rt && Opts.Rt->MayReturn;
277 if (MayReturn) {
278 IRB.CreateBr(Cont);
279 } else {
280 TrapCall->setDoesNotReturn();
281 IRB.CreateUnreachable();
282 }
283 // The preserve-all logic is somewhat duplicated in CGExpr.cpp for
284 // local-bounds. Make sure to change that too.
285 if (Opts.Rt && Opts.Rt->HandlerPreserveAllRegs && MayReturn)
287 if (!MayReturn && SingleTrap && !DebugTrapBB)
288 ReuseTrapBB = TrapBB;
289
290 return TrapBB;
291 };
292
293 for (const auto &Entry : TrapInfo) {
294 Instruction *Inst = Entry.first;
295 BuilderTy IRB(Inst->getIterator(), TargetFolder(DL));
296 insertBoundsCheck(Entry.second, IRB, GetTrapBB);
297 }
298
299 return !TrapInfo.empty();
300}
301
303 auto &TLI = AM.getResult<TargetLibraryAnalysis>(F);
304 auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
305
306 const InstrumentationOptions &CLOpts =
307 F.getContext().getLibraryOptions().get<InstrumentationOptions>();
308 if (!addBoundsChecking(F, TLI, SE, Opts, CLOpts.bounds_checking_single_trap))
309 return PreservedAnalyses::all();
310
312}
313
315 raw_ostream &OS, function_ref<StringRef(StringRef)> MapClassName2PassName) {
316 static_cast<PassInfoMixin<BoundsCheckingPass> *>(this)->printPipeline(
317 OS, MapClassName2PassName);
318 OS << "<";
319 if (Opts.Rt) {
320 if (Opts.Rt->MinRuntime)
321 OS << "min-";
322 OS << "rt";
323 if (!Opts.Rt->MayReturn)
324 OS << "-abort";
325 } else {
326 OS << "trap";
327 }
328 if (Opts.Merge)
329 OS << ";merge";
330 if (Opts.GuardKind)
331 OS << ";guard=" << static_cast<int>(*Opts.GuardKind);
332 OS << ">";
333}
uint64_t uint64_t
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB)
Adds run-time bounds checks to memory accessing instructions.
static CallInst * InsertCall(BuilderTy &IRB, bool MayReturn, StringRef Name)
static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI, ScalarEvolution &SE, const BoundsCheckingPass::Options &Opts, bool SingleTrap)
static Value * getBoundsCheckCond(Value *Ptr, Value *InstVal, const DataLayout &DL, TargetLibraryInfo &TLI, ObjectSizeOffsetEvaluator &ObjSizeEval, BuilderTy &IRB, ScalarEvolution &SE)
Gets the conditions under which memory accessing instructions will overflow.
static std::string getRuntimeCallName(const BoundsCheckingPass::Options::Runtime &Opts)
static CallInst * InsertTrap(BuilderTy &IRB, bool DebugTrapBB, std::optional< int8_t > GuardKind)
IRBuilder< TargetFolder, NoSanitizeInserter > BuilderTy
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
#define LLVM_DEBUG(...)
Definition Debug.h:119
NoSanitizeInserter()=default
void InsertHelper(Instruction *I, const Twine &Name, BasicBlock::iterator InsertPt) const
bool slt(const APInt &RHS) const
Signed less than comparison.
Definition APInt.h:1134
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
An instruction that atomically checks whether a specified value is in a memory location,...
an instruction that atomically reads a memory location, combines it with another value,...
LLVM Basic Block Representation.
Definition BasicBlock.h:62
LLVM_ABI BasicBlock * splitBasicBlock(iterator I, const Twine &BBName="")
Split the basic block into two basic blocks at the specified instruction.
const Function * getParent() const
Return the enclosing method, or null if none.
Definition BasicBlock.h:213
static BasicBlock * Create(LLVMContext &Context, const Twine &Name="", Function *Parent=nullptr, BasicBlock *InsertBefore=nullptr)
Creates a new BasicBlock.
Definition BasicBlock.h:206
InstListType::iterator iterator
Instruction iterators...
Definition BasicBlock.h:170
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction; assumes that the block is well-formed.
Definition BasicBlock.h:237
LLVM_ABI PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM)
LLVM_ABI void printPipeline(raw_ostream &OS, function_ref< StringRef(StringRef)> MapClassName2PassName)
void setCallingConv(CallingConv::ID CC)
void setDoesNotReturn()
void setDoesNotThrow()
void addFnAttr(Attribute::AttrKind Kind)
Adds the attribute to the function.
This class represents a function call, abstracting a target machine's calling convention.
static CondBrInst * Create(Value *Cond, BasicBlock *IfTrue, BasicBlock *IfFalse, InsertPosition InsertBefore=nullptr)
This is the shared class of boolean and integer constants.
Definition Constants.h:87
static LLVM_ABI ConstantInt * getFalse(LLVMContext &Context)
const APInt & getValue() const
Return the constant as an APInt value reference.
Definition Constants.h:159
A parsed version of the target data layout string in and methods for querying it.
Definition DataLayout.h:64
A debug info location.
Definition DebugLoc.h:126
A handy container for a FunctionType+Callee-pointer pair, which can be passed around as a single enti...
size_t size() const
Definition Function.h:843
LLVMContext & getContext() const
getContext - Return a reference to the LLVMContext associated with this function.
Definition Function.cpp:352
Module * getParent()
Get the module that this global value is contained inside of...
LLVM_ABI CallInst * CreateIntrinsicWithoutFolding(Intrinsic::ID ID, ArrayRef< Type * > OverloadTypes, ArrayRef< Value * > Args, FMFSource FMFSource={}, const Twine &Name="", ArrayRef< OperandBundleDef > OpBundles={})
Create a call to intrinsic ID with Args, mangled using OverloadTypes.
Value * CreateICmpULT(Value *LHS, Value *RHS, const Twine &Name="")
Definition IRBuilder.h:2398
IntegerType * getInt1Ty()
Fetch the type representing a single bit.
Definition IRBuilder.h:507
BasicBlock::iterator GetInsertPoint() const
Definition IRBuilder.h:176
BasicBlock * GetInsertBlock() const
Definition IRBuilder.h:173
Value * CreateSub(Value *LHS, Value *RHS, const Twine &Name="", bool HasNUW=false, bool HasNSW=false)
Definition IRBuilder.h:1435
Value * CreateAnd(Value *LHS, Value *RHS, const Twine &Name="")
Definition IRBuilder.h:1566
LLVM_ABI Value * CreateIntrinsic(Intrinsic::ID ID, ArrayRef< Type * > OverloadTypes, ArrayRef< Value * > Args, FMFSource FMFSource={}, const Twine &Name="", ArrayRef< OperandBundleDef > OpBundles={}, function_ref< void(CallInst *)> SetFn=[](CallInst *) {})
Variant to create a possibly constant-folded intrinsic.
CallInst * CreateCall(FunctionType *FTy, Value *Callee, ArrayRef< Value * > Args={}, const Twine &Name="", MDNode *FPMathTag=nullptr)
Definition IRBuilder.h:2561
Value * CreateICmpSLT(Value *LHS, Value *RHS, const Twine &Name="")
Definition IRBuilder.h:2414
LLVM_ABI Value * CreateTypeSize(Type *Ty, TypeSize Size)
Create an expression which evaluates to the number of units in Size at runtime.
Value * CreateOr(Value *LHS, Value *RHS, const Twine &Name="", bool IsDisjoint=false)
Definition IRBuilder.h:1588
IntegerType * getInt8Ty()
Fetch the type representing an 8-bit integer.
Definition IRBuilder.h:512
This provides the default implementation of the IRBuilder 'InsertHelper' method that is called whenev...
Definition IRBuilder.h:62
void InsertHelper(Instruction *I, const Twine &Name, BasicBlock::iterator InsertPt) const
Definition IRBuilder.h:64
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2909
LLVM_ABI InstListType::iterator eraseFromParent()
This method unlinks 'this' from the containing basic block and deletes it.
void setDebugLoc(DebugLoc Loc)
Set the debug location information for this instruction.
This is an important class for using LLVM in a threaded context.
Definition LLVMContext.h:76
An instruction for reading from memory.
Metadata node.
Definition Metadata.h:1081
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition Metadata.h:1579
FunctionCallee getOrInsertFunction(StringRef Name, FunctionType *T, AttributeList AttributeList)
Look up the specified function in the module symbol table.
Definition Module.cpp:211
Evaluate the size and offset of an object pointed to by a Value*.
LLVM_ABI SizeOffsetValue compute(Value *V)
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
Analysis pass that exposes the ScalarEvolution for a function.
The main scalar evolution driver.
LLVM_ABI const SCEV * getSCEV(Value *V)
Return a SCEV expression for the full generality of the specified expression.
ConstantRange getUnsignedRange(const SCEV *S)
Determine the unsigned range for a particular SCEV.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
An instruction for storing to memory.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
TargetFolder - Create constants with target dependent folding.
Analysis pass providing the TargetLibraryInfo.
Provides information about what library functions are available for the current target.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition Twine.h:82
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
static LLVM_ABI Type * getVoidTy(LLVMContext &C)
Definition Type.cpp:272
static UncondBrInst * Create(BasicBlock *Target, InsertPosition InsertBefore=nullptr)
LLVM Value Representation.
Definition Value.h:75
Type * getType() const
All values are typed, get the type of this value.
Definition Value.h:257
LLVMContext & getContext() const
All values hold a context through their type.
Definition Value.h:260
An efficient, type-erasing, non-owning reference to a callable.
self_iterator getIterator()
Definition ilist_node.h:123
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
@ PreserveAll
Used for runtime calls that preserves (almost) all registers.
Definition CallingConv.h:66
This is an optimization pass for GlobalISel generic memory operations.
@ Offset
Definition DWP.cpp:577
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition Debug.cpp:209
AnalysisManager< Function > FunctionAnalysisManager
Convenience typedef for the Function analysis manager.
std::optional< Runtime > Rt
std::optional< int8_t > GuardKind
Various options to control the behavior of getObjectSize.
Mode EvalMode
How we want to evaluate this object's size.
bool RoundToAlign
Whether to round the result up to the alignment of allocas, byval arguments, and global variables.
@ ExactUnderlyingSizeAndOffset
All branches must be known and have the same underlying size and offset to be merged.