blob: c92ae2697ea1b10af3b4d55932874784d7ecc6cf [file] [log] [blame]
Nuno Lopes5c525b52012-05-22 17:19:09 +00001//===- BoundsChecking.cpp - Instrumentation for run-time bounds checking --===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements a pass that instruments the code to perform run-time
11// bounds checking on loads, stores, and other memory intrinsics.
12//
13//===----------------------------------------------------------------------===//
14
15#define DEBUG_TYPE "bounds-checking"
16#include "llvm/Transforms/Scalar.h"
17#include "llvm/ADT/Statistic.h"
18#include "llvm/Analysis/MemoryBuiltins.h"
19#include "llvm/Support/Debug.h"
20#include "llvm/Support/InstIterator.h"
21#include "llvm/Support/IRBuilder.h"
22#include "llvm/Support/TargetFolder.h"
23#include "llvm/Target/TargetData.h"
24#include "llvm/Transforms/Utils/Local.h"
25#include "llvm/GlobalVariable.h"
26#include "llvm/Instructions.h"
27#include "llvm/Intrinsics.h"
28#include "llvm/Operator.h"
29#include "llvm/Pass.h"
30using namespace llvm;
31
32STATISTIC(ChecksAdded, "Bounds checks added");
33STATISTIC(ChecksSkipped, "Bounds checks skipped");
34STATISTIC(ChecksUnable, "Bounds checks unable to add");
35
36typedef IRBuilder<true, TargetFolder> BuilderTy;
37
38namespace {
39 enum ConstTriState {
40 NotConst, Const, Dunno
41 };
42
43 struct BoundsChecking : public FunctionPass {
44 const TargetData *TD;
45 BuilderTy *Builder;
46 Function *Fn;
47 BasicBlock *TrapBB;
48 unsigned Penalty;
49 static char ID;
50
51 BoundsChecking(unsigned _Penalty = 5) : FunctionPass(ID), Penalty(_Penalty){
52 initializeBoundsCheckingPass(*PassRegistry::getPassRegistry());
53 }
54
55 BasicBlock *getTrapBB();
56 ConstTriState computeAllocSize(Value *Alloc, uint64_t &Size, Value* &SizeValue);
57 bool instrument(Value *Ptr, Value *Val);
58
59 virtual bool runOnFunction(Function &F);
60
61 virtual void getAnalysisUsage(AnalysisUsage &AU) const {
62 AU.addRequired<TargetData>();
63 }
64 };
65}
66
67char BoundsChecking::ID = 0;
68INITIALIZE_PASS(BoundsChecking, "bounds-checking", "Run-time bounds checking",
69 false, false)
70
71
72/// getTrapBB - create a basic block that traps. All overflowing conditions
73/// branch to this block. There's only one trap block per function.
74BasicBlock *BoundsChecking::getTrapBB() {
75 if (TrapBB)
76 return TrapBB;
77
78 BasicBlock::iterator PrevInsertPoint = Builder->GetInsertPoint();
79 TrapBB = BasicBlock::Create(Fn->getContext(), "trap", Fn);
80 Builder->SetInsertPoint(TrapBB);
81
82 llvm::Value *F = Intrinsic::getDeclaration(Fn->getParent(), Intrinsic::trap);
83 CallInst *TrapCall = Builder->CreateCall(F);
84 TrapCall->setDoesNotReturn();
85 TrapCall->setDoesNotThrow();
86 Builder->CreateUnreachable();
87
88 Builder->SetInsertPoint(PrevInsertPoint);
89 return TrapBB;
90}
91
92
93/// computeAllocSize - compute the object size allocated by an allocation
94/// site. Returns NotConst if the size is not constant (in SizeValue), Const if
95/// the size is constant (in Size), and Dunno if the size could not be
96/// determined within the given maximum Penalty that the computation would
97/// incurr at run-time.
98ConstTriState BoundsChecking::computeAllocSize(Value *Alloc, uint64_t &Size,
99 Value* &SizeValue) {
100 if (GlobalVariable *GV = dyn_cast<GlobalVariable>(Alloc)) {
101 if (GV->hasDefinitiveInitializer()) {
102 Constant *C = GV->getInitializer();
103 Size = TD->getTypeAllocSize(C->getType());
104 return Const;
105 }
106 return Dunno;
107
108 } else if (AllocaInst *AI = dyn_cast<AllocaInst>(Alloc)) {
109 if (!AI->getAllocatedType()->isSized())
110 return Dunno;
111
112 Size = TD->getTypeAllocSize(AI->getAllocatedType());
113 if (!AI->isArrayAllocation())
114 return Const; // we are done
115
116 Value *ArraySize = AI->getArraySize();
117 if (const ConstantInt *C = dyn_cast<ConstantInt>(ArraySize)) {
118 Size *= C->getZExtValue();
119 return Const;
120 }
121
122 if (Penalty < 2)
123 return Dunno;
124
125 SizeValue = ConstantInt::get(ArraySize->getType(), Size);
126 SizeValue = Builder->CreateMul(SizeValue, ArraySize);
127 return NotConst;
128
129 } else if (CallInst *MI = extractMallocCall(Alloc)) {
130 SizeValue = MI->getArgOperand(0);
131 if (ConstantInt *CI = dyn_cast<ConstantInt>(SizeValue)) {
132 Size = CI->getZExtValue();
133 return Const;
134 }
135 return Penalty >= 2 ? NotConst : Dunno;
136
137 } else if (CallInst *MI = extractCallocCall(Alloc)) {
138 Value *Arg1 = MI->getArgOperand(0);
139 Value *Arg2 = MI->getArgOperand(1);
140 if (ConstantInt *CI1 = dyn_cast<ConstantInt>(Arg1)) {
141 if (ConstantInt *CI2 = dyn_cast<ConstantInt>(Arg2)) {
142 Size = (CI1->getValue() * CI2->getValue()).getZExtValue();
143 return Const;
144 }
145 }
146
147 if (Penalty < 2)
148 return Dunno;
149
150 SizeValue = Builder->CreateMul(Arg1, Arg2);
151 return NotConst;
152 }
153
154 DEBUG(dbgs() << "computeAllocSize failed:\n" << *Alloc);
155 return Dunno;
156}
157
158
159bool BoundsChecking::instrument(Value *Ptr, Value *InstVal) {
160 uint64_t NeededSize = TD->getTypeStoreSize(InstVal->getType());
161 DEBUG(dbgs() << "Instrument " << *Ptr << " for " << Twine(NeededSize)
162 << " bytes\n");
163
164 Type *SizeTy = Type::getInt64Ty(Fn->getContext());
165
166 // Get to the real allocated thing and offset as fast as possible.
167 Ptr = Ptr->stripPointerCasts();
168 GEPOperator *GEP;
169
170 if ((GEP = dyn_cast<GEPOperator>(Ptr))) {
171 // check if we will be able to get the offset
172 if (!GEP->hasAllConstantIndices() && Penalty < 2) {
173 ++ChecksUnable;
174 return false;
175 }
176 Ptr = GEP->getPointerOperand()->stripPointerCasts();
177 }
178
179 uint64_t Size = 0;
180 Value *SizeValue = 0;
181 ConstTriState ConstAlloc = computeAllocSize(Ptr, Size, SizeValue);
182 if (ConstAlloc == Dunno) {
183 ++ChecksUnable;
184 return false;
185 }
186 assert(ConstAlloc == Const || SizeValue);
187
188 uint64_t Offset = 0;
189 Value *OffsetValue = 0;
190
191 if (GEP) {
192 if (GEP->hasAllConstantIndices()) {
193 SmallVector<Value*, 8> Ops(GEP->idx_begin(), GEP->idx_end());
194 assert(GEP->getPointerOperandType()->isPointerTy());
195 Offset = TD->getIndexedOffset(GEP->getPointerOperandType(), Ops);
196 } else {
197 OffsetValue = EmitGEPOffset(Builder, *TD, GEP);
198 }
199 }
200
201 if (!OffsetValue && ConstAlloc == Const) {
202 if (Size < Offset || (Size - Offset) < NeededSize) {
203 // Out of bounds
204 Builder->CreateBr(getTrapBB());
205 ++ChecksAdded;
206 return true;
207 }
208 // in bounds
209 ++ChecksSkipped;
210 return false;
211 }
212
213 if (OffsetValue)
214 OffsetValue = Builder->CreateZExt(OffsetValue, SizeTy);
215 else
216 OffsetValue = ConstantInt::get(SizeTy, Offset);
217
218 if (SizeValue)
219 SizeValue = Builder->CreateZExt(SizeValue, SizeTy);
220 else
221 SizeValue = ConstantInt::get(SizeTy, Size);
222
223 Value *NeededSizeVal = ConstantInt::get(SizeTy, NeededSize);
224 Value *ObjSize = Builder->CreateSub(SizeValue, OffsetValue);
225 Value *Cmp1 = Builder->CreateICmpULT(SizeValue, OffsetValue);
226 Value *Cmp2 = Builder->CreateICmpULT(ObjSize, NeededSizeVal);
227 Value *Or = Builder->CreateOr(Cmp1, Cmp2);
228
229 // FIXME: add unlikely branch taken metadata?
230 Instruction *Inst = Builder->GetInsertPoint();
231 BasicBlock *OldBB = Inst->getParent();
232 BasicBlock *Cont = OldBB->splitBasicBlock(Inst);
233 OldBB->getTerminator()->eraseFromParent();
234 BranchInst::Create(getTrapBB(), Cont, Or, OldBB);
235 ++ChecksAdded;
236 return true;
237}
238
239bool BoundsChecking::runOnFunction(Function &F) {
240 TD = &getAnalysis<TargetData>();
241
242 TrapBB = 0;
243 Fn = &F;
244 BuilderTy TheBuilder(F.getContext(), TargetFolder(TD));
245 Builder = &TheBuilder;
246
247 // check HANDLE_MEMORY_INST in include/llvm/Instruction.def for memory
248 // touching instructions
249 std::vector<Instruction*> WorkList;
250 for (inst_iterator i = inst_begin(F), e = inst_end(F); i != e; ++i) {
251 Instruction *I = &*i;
252 if (isa<LoadInst>(I) || isa<StoreInst>(I) || isa<AtomicCmpXchgInst>(I) ||
253 isa<AtomicRMWInst>(I))
254 WorkList.push_back(I);
255 }
256
257 bool MadeChange = false;
258 while (!WorkList.empty()) {
259 Instruction *I = WorkList.back();
260 WorkList.pop_back();
261
262 Builder->SetInsertPoint(I);
263 if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
264 MadeChange |= instrument(LI->getPointerOperand(), LI);
265 } else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
266 MadeChange |= instrument(SI->getPointerOperand(), SI->getValueOperand());
267 } else if (AtomicCmpXchgInst *AI = dyn_cast<AtomicCmpXchgInst>(I)) {
268 MadeChange |= instrument(AI->getPointerOperand(),AI->getCompareOperand());
269 } else if (AtomicRMWInst *AI = dyn_cast<AtomicRMWInst>(I)) {
270 MadeChange |= instrument(AI->getPointerOperand(), AI->getValOperand());
271 } else {
272 llvm_unreachable("unknown Instruction type");
273 }
274 }
275 return MadeChange;
276}
277
278FunctionPass *llvm::createBoundsCheckingPass(unsigned Penalty) {
279 return new BoundsChecking(Penalty);
280}