blob: 008d2b7647c5fe9054bff6415cbecb9bad5be8ad [file] [log] [blame]
Vitaly Buka4493fe12018-11-26 21:57:47 +00001//===- StackSafetyAnalysis.cpp - Stack memory safety analysis -------------===//
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//===----------------------------------------------------------------------===//
11
12#include "llvm/Analysis/StackSafetyAnalysis.h"
Vitaly Bukafa98c072018-11-26 21:57:59 +000013#include "llvm/ADT/StringExtras.h"
Vitaly Buka4493fe12018-11-26 21:57:47 +000014#include "llvm/Analysis/ScalarEvolutionExpressions.h"
Vitaly Bukafa98c072018-11-26 21:57:59 +000015#include "llvm/IR/CallSite.h"
16#include "llvm/IR/InstIterator.h"
17#include "llvm/IR/IntrinsicInst.h"
Vitaly Buka4493fe12018-11-26 21:57:47 +000018#include "llvm/Support/raw_ostream.h"
19
20using namespace llvm;
21
22#define DEBUG_TYPE "stack-safety"
23
Vitaly Bukafa98c072018-11-26 21:57:59 +000024namespace {
Vitaly Buka4493fe12018-11-26 21:57:47 +000025
Vitaly Bukafa98c072018-11-26 21:57:59 +000026/// Rewrite an SCEV expression for a memory access address to an expression that
27/// represents offset from the given alloca.
28class AllocaOffsetRewriter : public SCEVRewriteVisitor<AllocaOffsetRewriter> {
29 const Value *AllocaPtr;
30
31public:
32 AllocaOffsetRewriter(ScalarEvolution &SE, const Value *AllocaPtr)
33 : SCEVRewriteVisitor(SE), AllocaPtr(AllocaPtr) {}
34
35 const SCEV *visit(const SCEV *Expr) {
36 // Only re-write the expression if the alloca is used in an addition
37 // expression (it can be used in other types of expressions if it's cast to
38 // an int and passed as an argument.)
39 if (!isa<SCEVAddRecExpr>(Expr) && !isa<SCEVAddExpr>(Expr) &&
40 !isa<SCEVUnknown>(Expr))
41 return Expr;
42 return SCEVRewriteVisitor<AllocaOffsetRewriter>::visit(Expr);
43 }
44
45 const SCEV *visitUnknown(const SCEVUnknown *Expr) {
46 // FIXME: look through one or several levels of definitions?
47 // This can be inttoptr(AllocaPtr) and SCEV would not unwrap
48 // it for us.
49 if (Expr->getValue() == AllocaPtr)
50 return SE.getZero(Expr->getType());
51 return Expr;
52 }
53};
54
55/// Describes use of address in as a function call argument.
56struct PassAsArgInfo {
57 /// Function being called.
58 const GlobalValue *Callee = nullptr;
59 /// Index of argument which pass address.
60 size_t ParamNo = 0;
61 // Offset range of address from base address (alloca or calling function
62 // argument).
63 // Range should never set to empty-set, that is an invalid access range
64 // that can cause empty-set to be propagated with ConstantRange::add
65 ConstantRange Offset;
66 PassAsArgInfo(const GlobalValue *Callee, size_t ParamNo, ConstantRange Offset)
67 : Callee(Callee), ParamNo(ParamNo), Offset(Offset) {}
68
69 StringRef getName() const { return Callee->getName(); }
70};
71
72raw_ostream &operator<<(raw_ostream &OS, const PassAsArgInfo &P) {
73 return OS << "@" << P.getName() << "(arg" << P.ParamNo << ", " << P.Offset
74 << ")";
75}
76
77/// Describe uses of address (alloca or parameter) inside of the function.
78struct UseInfo {
79 // Access range if the address (alloca or parameters).
80 // It is allowed to be empty-set when there are no known accesses.
81 ConstantRange Range;
82
83 // List of calls which pass address as an argument.
84 SmallVector<PassAsArgInfo, 4> Calls;
85
86 explicit UseInfo(unsigned PointerSize) : Range{PointerSize, false} {}
87
88 void updateRange(ConstantRange R) { Range = Range.unionWith(R); }
89};
90
91raw_ostream &operator<<(raw_ostream &OS, const UseInfo &U) {
92 OS << U.Range;
93 for (auto &Call : U.Calls)
94 OS << ", " << Call;
95 return OS;
96}
97
98struct AllocaInfo {
99 const AllocaInst *AI = nullptr;
100 uint64_t Size = 0;
101 UseInfo Use;
102
103 AllocaInfo(unsigned PointerSize, const AllocaInst *AI, uint64_t Size)
104 : AI(AI), Size(Size), Use(PointerSize) {}
105
106 StringRef getName() const { return AI->getName(); }
107};
108
109raw_ostream &operator<<(raw_ostream &OS, const AllocaInfo &A) {
110 return OS << A.getName() << "[" << A.Size << "]: " << A.Use;
111}
112
113struct ParamInfo {
114 const Argument *Arg = nullptr;
115 UseInfo Use;
116
117 explicit ParamInfo(unsigned PointerSize, const Argument *Arg)
118 : Arg(Arg), Use(PointerSize) {}
119
120 StringRef getName() const { return Arg ? Arg->getName() : "<N/A>"; }
121};
122
123raw_ostream &operator<<(raw_ostream &OS, const ParamInfo &P) {
124 return OS << P.getName() << "[]: " << P.Use;
125}
126
127/// Calculate the allocation size of a given alloca. Returns 0 if the
128/// size can not be statically determined.
129uint64_t getStaticAllocaAllocationSize(const AllocaInst *AI) {
130 const DataLayout &DL = AI->getModule()->getDataLayout();
131 uint64_t Size = DL.getTypeAllocSize(AI->getAllocatedType());
132 if (AI->isArrayAllocation()) {
133 auto C = dyn_cast<ConstantInt>(AI->getArraySize());
134 if (!C)
135 return 0;
136 Size *= C->getZExtValue();
137 }
138 return Size;
139}
140
141} // end anonymous namespace
142
143/// Describes uses of allocas and parameters inside of a single function.
144struct StackSafetyInfo::FunctionInfo {
145 // May be a Function or a GlobalAlias
146 const GlobalValue *GV = nullptr;
147 // Informations about allocas uses.
148 SmallVector<AllocaInfo, 4> Allocas;
149 // Informations about parameters uses.
150 SmallVector<ParamInfo, 4> Params;
151 // TODO: describe return value as depending on one or more of its arguments.
152
153 FunctionInfo(const StackSafetyInfo &SSI) : FunctionInfo(*SSI.Info) {}
154
155 explicit FunctionInfo(const Function *F) : GV(F){};
156
157 FunctionInfo(FunctionInfo &&) = default;
158
159 bool IsDSOLocal() const { return GV->isDSOLocal(); };
160
161 bool IsInterposable() const { return GV->isInterposable(); };
162
163 StringRef getName() const { return GV->getName(); }
164
165 void print(raw_ostream &O) const {
166 O << " @" << getName() << (IsDSOLocal() ? "" : " dso_preemptable")
167 << (IsInterposable() ? " interposable" : "") << "\n";
168 O << " args uses:\n";
169 for (auto &P : Params)
170 O << " " << P << "\n";
171 O << " allocas uses:\n";
172 for (auto &AS : Allocas)
173 O << " " << AS << "\n";
174 }
175
176private:
177 FunctionInfo(const FunctionInfo &) = default;
178};
179
180namespace {
181
182class StackSafetyLocalAnalysis {
183 const Function &F;
184 const DataLayout &DL;
185 ScalarEvolution &SE;
186 unsigned PointerSize = 0;
187
188 const ConstantRange UnknownRange;
189
190 ConstantRange offsetFromAlloca(Value *Addr, const Value *AllocaPtr);
191 ConstantRange getAccessRange(Value *Addr, const Value *AllocaPtr,
192 uint64_t AccessSize);
193 ConstantRange getMemIntrinsicAccessRange(const MemIntrinsic *MI, const Use &U,
194 const Value *AllocaPtr);
195
196 bool analyzeAllUses(const Value *Ptr, UseInfo &AS);
197
198 ConstantRange getRange(uint64_t Lower, uint64_t Upper) const {
199 return ConstantRange(APInt(PointerSize, Lower), APInt(PointerSize, Upper));
200 }
201
202public:
203 StackSafetyLocalAnalysis(const Function &F, ScalarEvolution &SE)
204 : F(F), DL(F.getParent()->getDataLayout()), SE(SE),
205 PointerSize(DL.getPointerSizeInBits()),
206 UnknownRange(PointerSize, true) {}
207
208 // Run the transformation on the associated function.
209 StackSafetyInfo run();
210};
211
212ConstantRange
213StackSafetyLocalAnalysis::offsetFromAlloca(Value *Addr,
214 const Value *AllocaPtr) {
215 if (!SE.isSCEVable(Addr->getType()))
216 return UnknownRange;
217
218 AllocaOffsetRewriter Rewriter(SE, AllocaPtr);
219 const SCEV *Expr = Rewriter.visit(SE.getSCEV(Addr));
220 ConstantRange Offset = SE.getUnsignedRange(Expr).zextOrTrunc(PointerSize);
221 assert(!Offset.isEmptySet());
222 return Offset;
223}
224
225ConstantRange StackSafetyLocalAnalysis::getAccessRange(Value *Addr,
226 const Value *AllocaPtr,
227 uint64_t AccessSize) {
228 if (!SE.isSCEVable(Addr->getType()))
229 return UnknownRange;
230
231 AllocaOffsetRewriter Rewriter(SE, AllocaPtr);
232 const SCEV *Expr = Rewriter.visit(SE.getSCEV(Addr));
233
234 ConstantRange AccessStartRange =
235 SE.getUnsignedRange(Expr).zextOrTrunc(PointerSize);
236 ConstantRange SizeRange = getRange(0, AccessSize);
237 ConstantRange AccessRange = AccessStartRange.add(SizeRange);
238 assert(!AccessRange.isEmptySet());
239 return AccessRange;
240}
241
242ConstantRange StackSafetyLocalAnalysis::getMemIntrinsicAccessRange(
243 const MemIntrinsic *MI, const Use &U, const Value *AllocaPtr) {
244 if (auto MTI = dyn_cast<MemTransferInst>(MI)) {
245 if (MTI->getRawSource() != U && MTI->getRawDest() != U)
246 return getRange(0, 1);
247 } else {
248 if (MI->getRawDest() != U)
249 return getRange(0, 1);
250 }
251 const auto *Len = dyn_cast<ConstantInt>(MI->getLength());
252 // Non-constant size => unsafe. FIXME: try SCEV getRange.
253 if (!Len)
254 return UnknownRange;
255 ConstantRange AccessRange = getAccessRange(U, AllocaPtr, Len->getZExtValue());
256 return AccessRange;
257}
258
259/// The function analyzes all local uses of Ptr (alloca or argument) and
260/// calculates local access range and all function calls where it was used.
261bool StackSafetyLocalAnalysis::analyzeAllUses(const Value *Ptr, UseInfo &US) {
262 SmallPtrSet<const Value *, 16> Visited;
263 SmallVector<const Value *, 8> WorkList;
264 WorkList.push_back(Ptr);
265
266 // A DFS search through all uses of the alloca in bitcasts/PHI/GEPs/etc.
267 while (!WorkList.empty()) {
268 const Value *V = WorkList.pop_back_val();
269 for (const Use &UI : V->uses()) {
270 auto I = cast<const Instruction>(UI.getUser());
271 assert(V == UI.get());
272
273 switch (I->getOpcode()) {
274 case Instruction::Load: {
275 US.updateRange(
276 getAccessRange(UI, Ptr, DL.getTypeStoreSize(I->getType())));
277 break;
278 }
279
280 case Instruction::VAArg:
281 // "va-arg" from a pointer is safe.
282 break;
283 case Instruction::Store: {
284 if (V == I->getOperand(0)) {
285 // Stored the pointer - conservatively assume it may be unsafe.
286 US.updateRange(UnknownRange);
287 return false;
288 }
289 US.updateRange(getAccessRange(
290 UI, Ptr, DL.getTypeStoreSize(I->getOperand(0)->getType())));
291 break;
292 }
293
294 case Instruction::Ret:
295 // Information leak.
296 // FIXME: Process parameters correctly. This is a leak only if we return
297 // alloca.
298 US.updateRange(UnknownRange);
299 return false;
300
301 case Instruction::Call:
302 case Instruction::Invoke: {
303 ImmutableCallSite CS(I);
304
305 if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
306 if (II->getIntrinsicID() == Intrinsic::lifetime_start ||
307 II->getIntrinsicID() == Intrinsic::lifetime_end)
308 break;
309 }
310
311 if (const MemIntrinsic *MI = dyn_cast<MemIntrinsic>(I)) {
312 US.updateRange(getMemIntrinsicAccessRange(MI, UI, Ptr));
313 break;
314 }
315
316 // FIXME: consult devirt?
317 // Do not follow aliases, otherwise we could inadvertently follow
318 // dso_preemptable aliases or aliases with interposable linkage.
319 const GlobalValue *Callee = dyn_cast<GlobalValue>(
320 CS.getCalledValue()->stripPointerCastsNoFollowAliases());
321 if (!Callee) {
322 US.updateRange(UnknownRange);
323 return false;
324 }
325
326 assert(isa<Function>(Callee) || isa<GlobalAlias>(Callee));
327
328 ImmutableCallSite::arg_iterator B = CS.arg_begin(), E = CS.arg_end();
329 for (ImmutableCallSite::arg_iterator A = B; A != E; ++A) {
330 if (A->get() == V) {
331 ConstantRange Offset = offsetFromAlloca(UI, Ptr);
332 US.Calls.emplace_back(Callee, A - B, Offset);
333 }
334 }
335
336 break;
337 }
338
339 default:
340 if (Visited.insert(I).second)
341 WorkList.push_back(cast<const Instruction>(I));
342 }
343 }
344 }
345
346 return true;
347}
348
349StackSafetyInfo StackSafetyLocalAnalysis::run() {
350 StackSafetyInfo::FunctionInfo Info(&F);
351 assert(!F.isDeclaration() &&
352 "Can't run StackSafety on a function declaration");
353
354 LLVM_DEBUG(dbgs() << "[StackSafety] " << F.getName() << "\n");
355
356 for (auto &I : instructions(F)) {
357 if (auto AI = dyn_cast<AllocaInst>(&I)) {
358 Info.Allocas.emplace_back(PointerSize, AI,
359 getStaticAllocaAllocationSize(AI));
360 AllocaInfo &AS = Info.Allocas.back();
361 analyzeAllUses(AI, AS.Use);
362 }
363 }
364
365 for (const Argument &A : make_range(F.arg_begin(), F.arg_end())) {
366 Info.Params.emplace_back(PointerSize, &A);
367 ParamInfo &PS = Info.Params.back();
368 analyzeAllUses(&A, PS.Use);
369 }
370
371 LLVM_DEBUG(dbgs() << "[StackSafety] done\n");
372 LLVM_DEBUG(Info.print(dbgs()));
373 return StackSafetyInfo(std::move(Info));
374}
375
Vitaly Bukab8e6fa62018-11-26 23:05:48 +0000376void print(const StackSafetyGlobalInfo &SSI, raw_ostream &O, const Module &M) {
377 O << "Not Implemented\n";
378}
379
Vitaly Bukafa98c072018-11-26 21:57:59 +0000380} // end anonymous namespace
381
382StackSafetyInfo::StackSafetyInfo() = default;
383StackSafetyInfo::StackSafetyInfo(StackSafetyInfo &&) = default;
384StackSafetyInfo &StackSafetyInfo::operator=(StackSafetyInfo &&) = default;
385
386StackSafetyInfo::StackSafetyInfo(FunctionInfo &&Info)
387 : Info(new FunctionInfo(std::move(Info))) {}
388
389StackSafetyInfo::~StackSafetyInfo() = default;
390
391void StackSafetyInfo::print(raw_ostream &O) const { Info->print(O); }
392
393AnalysisKey StackSafetyAnalysis::Key;
Vitaly Buka4493fe12018-11-26 21:57:47 +0000394
395StackSafetyInfo StackSafetyAnalysis::run(Function &F,
396 FunctionAnalysisManager &AM) {
Vitaly Bukafa98c072018-11-26 21:57:59 +0000397 StackSafetyLocalAnalysis SSLA(F, AM.getResult<ScalarEvolutionAnalysis>(F));
398 return SSLA.run();
Vitaly Buka4493fe12018-11-26 21:57:47 +0000399}
400
401PreservedAnalyses StackSafetyPrinterPass::run(Function &F,
402 FunctionAnalysisManager &AM) {
403 OS << "'Stack Safety Local Analysis' for function '" << F.getName() << "'\n";
404 AM.getResult<StackSafetyAnalysis>(F).print(OS);
405 return PreservedAnalyses::all();
406}
407
408char StackSafetyInfoWrapperPass::ID = 0;
409
410StackSafetyInfoWrapperPass::StackSafetyInfoWrapperPass() : FunctionPass(ID) {
411 initializeStackSafetyInfoWrapperPassPass(*PassRegistry::getPassRegistry());
412}
413
414void StackSafetyInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const {
415 AU.addRequired<ScalarEvolutionWrapperPass>();
416 AU.setPreservesAll();
417}
418
419void StackSafetyInfoWrapperPass::print(raw_ostream &O, const Module *M) const {
420 SSI.print(O);
421}
422
Vitaly Bukafa98c072018-11-26 21:57:59 +0000423bool StackSafetyInfoWrapperPass::runOnFunction(Function &F) {
424 StackSafetyLocalAnalysis SSLA(
425 F, getAnalysis<ScalarEvolutionWrapperPass>().getSE());
426 SSI = StackSafetyInfo(SSLA.run());
427 return false;
428}
Vitaly Buka4493fe12018-11-26 21:57:47 +0000429
Vitaly Bukab8e6fa62018-11-26 23:05:48 +0000430AnalysisKey StackSafetyGlobalAnalysis::Key;
431
432StackSafetyGlobalInfo
433StackSafetyGlobalAnalysis::run(Module &M, ModuleAnalysisManager &AM) {
434 return {};
435}
436
437PreservedAnalyses StackSafetyGlobalPrinterPass::run(Module &M,
438 ModuleAnalysisManager &AM) {
439 OS << "'Stack Safety Analysis' for module '" << M.getName() << "'\n";
440 print(AM.getResult<StackSafetyGlobalAnalysis>(M), OS, M);
441 return PreservedAnalyses::all();
442}
443
444char StackSafetyGlobalInfoWrapperPass::ID = 0;
445
446StackSafetyGlobalInfoWrapperPass::StackSafetyGlobalInfoWrapperPass()
447 : ModulePass(ID) {
448 initializeStackSafetyGlobalInfoWrapperPassPass(
449 *PassRegistry::getPassRegistry());
450}
451
452void StackSafetyGlobalInfoWrapperPass::print(raw_ostream &O,
453 const Module *M) const {
454 ::print(SSI, O, *M);
455}
456
457void StackSafetyGlobalInfoWrapperPass::getAnalysisUsage(
458 AnalysisUsage &AU) const {
459 AU.addRequired<StackSafetyInfoWrapperPass>();
460}
461
462bool StackSafetyGlobalInfoWrapperPass::runOnModule(Module &M) { return false; }
463
Vitaly Buka4493fe12018-11-26 21:57:47 +0000464static const char LocalPassArg[] = "stack-safety-local";
465static const char LocalPassName[] = "Stack Safety Local Analysis";
466INITIALIZE_PASS_BEGIN(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName,
467 false, true)
468INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
469INITIALIZE_PASS_END(StackSafetyInfoWrapperPass, LocalPassArg, LocalPassName,
470 false, true)
Vitaly Bukab8e6fa62018-11-26 23:05:48 +0000471
472static const char GlobalPassName[] = "Stack Safety Analysis";
473INITIALIZE_PASS_BEGIN(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE,
474 GlobalPassName, false, false)
475INITIALIZE_PASS_DEPENDENCY(StackSafetyInfoWrapperPass)
476INITIALIZE_PASS_END(StackSafetyGlobalInfoWrapperPass, DEBUG_TYPE,
477 GlobalPassName, false, false)