Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(350)

Side by Side Diff: lib/Transforms/MinSFI/ExpandAllocas.cpp

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/Transforms/MinSFI/CMakeLists.txt ('k') | lib/Transforms/MinSFI/LLVMBuild.txt » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 //===----- ExpandAllocas.cpp - Allocate memory on the untrusted stack -----===//
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 // Code sandboxed with MinSFI cannot access the execution stack directly
11 // because the stack lies outside of its address subspace, which prevents it
12 // from using memory allocated with the alloca instruction. This pass therefore
13 // replaces allocas with memory allocation on a separate stack at a fixed
14 // location inside the designated memory region.
15 //
16 // The new stack does not have to be trusted as it is only used for memory
17 // allocation inside the sandbox. The call and ret instructions still operate
18 // on the native stack, preventing manipulation with the return address or
19 // callee-saved registers.
20 //
21 // This pass also replaces the @llvm.stacksave and @llvm.stackrestore
22 // intrinsics which would otherwise allow access to the native stack pointer.
23 // Instead, they are expanded out and save/restore the current untrusted stack
24 // pointer.
25 //
26 // When a function is invoked, the current untrusted stack pointer is obtained
27 // from the "__sfi_stack_ptr" global variable (internal to the module). The
28 // function then keeps track of the current value of the stack pointer, but
29 // must update the global variable prior to any function calls and restore the
30 // initial value before it returns.
31 //
32 // The stack pointer is initialized in the entry function of the module, the
33 // _start_minsfi function. The runtime is expected to copy the arguments
34 // (a NULL-terminated integer array) at the end of the allocated memory region,
35 // i.e. at the bottom of the untrusted stack, and pass the pointer to the array
36 // to the entry function. The sandboxed code is then expected to use the
37 // pointer not only to access its arguments but also as the initial value of
38 // its stack pointer and to grow the stack backwards.
39 //
40 // If an alloca requests alignment greater than 1, the untrusted stack pointer
41 // is aligned accordingly. However, the alignment is applied before the address
42 // is sandboxed and therefore the runtime must guarantee that the base address
43 // of the sandbox is aligned to at least 2^29 bytes (=512MB), which is the
44 // maximum alignment supported by LLVM.
45 //
46 // Possible optimizations:
47 // - accumulate constant-sized allocas to reduce the number of stores
48 // into the global stack pointer variable
49 // - remove stores into the global pointer if the respective values never
50 // reach a function call
51 // - align frame to 16 bytes
52 //
53 //===----------------------------------------------------------------------===//
54
55 #include "llvm/Pass.h"
56 #include "llvm/IR/Constants.h"
57 #include "llvm/IR/DataLayout.h"
58 #include "llvm/IR/Function.h"
59 #include "llvm/IR/Instructions.h"
60 #include "llvm/IR/IntrinsicInst.h"
61 #include "llvm/IR/Module.h"
62 #include "llvm/Support/MathExtras.h"
63 #include "llvm/Transforms/MinSFI.h"
64 #include "llvm/Transforms/NaCl.h"
65
66 using namespace llvm;
67
68 static const char InternalSymName_StackPointer[] = "__sfi_stack_ptr";
69
70 namespace {
71 // ExpandAllocas needs to be a ModulePass because it adds a GlobalVariable.
72 class ExpandAllocas : public ModulePass {
73 GlobalVariable *StackPtrVar;
74 Type *IntPtrType, *I8Ptr;
75
76 void runOnFunction(Function &Func);
77 void insertStackPtrInit(Module &M);
78
79 public:
80 static char ID;
81 ExpandAllocas() : ModulePass(ID), StackPtrVar(NULL), IntPtrType(NULL),
82 I8Ptr(NULL) {
83 initializeExpandAllocasPass(*PassRegistry::getPassRegistry());
84 }
85
86 virtual bool runOnModule(Module &M);
87 };
88 } // namespace
89
90 bool ExpandAllocas::runOnModule(Module &M) {
91 DataLayout DL(&M);
92 IntPtrType = DL.getIntPtrType(M.getContext());
93 I8Ptr = Type::getInt8PtrTy(M.getContext());
94
95 // Create the stack pointer global variable. We are forced to give it some
96 // initial value, but it will be initialized at runtime.
97 StackPtrVar = new GlobalVariable(M, IntPtrType, /*isConstant=*/false,
98 GlobalVariable::InternalLinkage,
99 ConstantInt::get(IntPtrType, 0),
100 InternalSymName_StackPointer);
101
102 for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func)
103 runOnFunction(*Func);
104
105 insertStackPtrInit(M);
106
107 return true;
108 }
109
110 static inline void replaceWithPointer(Instruction *OrigInst, Value *IntPtr,
111 SmallVectorImpl<Instruction*> &Dead) {
112 Instruction *NewInst =
113 new IntToPtrInst(IntPtr, OrigInst->getType(), "", OrigInst);
114 NewInst->takeName(OrigInst);
115 OrigInst->replaceAllUsesWith(NewInst);
116 CopyDebug(NewInst, OrigInst);
117 Dead.push_back(OrigInst);
118 }
119
120 static inline Instruction *getBBStackPtr(BasicBlock *BB) {
121 return BB->getInstList().begin();
122 }
123
124 void ExpandAllocas::runOnFunction(Function &Func) {
125 // Do an initial scan of the entire function body. Check whether it contains
126 // instructions which we want to operate on the untrusted stack and return
127 // if there aren't any. Also check whether it contains any function calls.
128 // If not, we will not have to update the global stack pointer variable.
129 bool NoUntrustedStackOps = true;
130 bool MustUpdateStackPtrGlobal = false;
131 for (Function::iterator BB = Func.begin(), E = Func.end(); BB != E; ++BB) {
132 for (BasicBlock::iterator Inst = BB->begin(), E = BB->end(); Inst != E;
133 ++Inst) {
134 NoUntrustedStackOps &= !isa<AllocaInst>(Inst);
135 if (CallInst *Call = dyn_cast<CallInst>(Inst)) {
136 if (isa<IntrinsicInst>(Call)) {
137 unsigned IntrinsicID = Call->getCalledFunction()->getIntrinsicID();
138 bool IsNotStackIntr = IntrinsicID != Intrinsic::stacksave &&
139 IntrinsicID != Intrinsic::stackrestore;
140 NoUntrustedStackOps &= IsNotStackIntr;
141 MustUpdateStackPtrGlobal |= IsNotStackIntr;
142 } else {
143 MustUpdateStackPtrGlobal = true;
144 }
145 }
146 }
147 }
148
149 if (NoUntrustedStackOps)
150 return;
151
152 SmallVector<Instruction *, 10> DeadInsts;
153 Instruction *InitialStackPtr = new LoadInst(StackPtrVar, "frame_top");
154
155 // First, we insert a new instruction at the beginning of each basic block,
156 // which will represent the value of the stack pointer at that point. For
157 // the entry block, this is the value of the global stack pointer variable.
158 // Other blocks are initialized with empty phi nodes which we will later
159 // fill with the values carried over from the respective predecessors.
160 BasicBlock *EntryBB = &Func.getEntryBlock();
161 for (Function::iterator BB = Func.begin(), E = Func.end(); BB != E; ++BB)
162 BB->getInstList().push_front(
163 ((BasicBlock*)BB == EntryBB) ? InitialStackPtr
164 : PHINode::Create(IntPtrType, 2, ""));
165
166 // Now iterate over the instructions and expand out the untrusted stack
167 // operations. Allocas are replaced with pointer arithmetic that pushes
168 // the untrusted stack pointer and updates the global stack pointer variable
169 // if the initial scan identified function calls in the code.
170 // The @llvm.stacksave intrinsic returns the latest value of the stack
171 // pointer, and the @llvm.stackrestore overwrites it and potentially updates
172 // the global variable. If needed, return instructions are prepended with
173 // a store which restores the initial value of the global variable.
174 // At the end of each basic block, the last value of the untrusted stack
175 // pointer is inserted into the phi node at the beginning of each successor
176 // block.
177 for (Function::iterator BB = Func.begin(), EBB = Func.end(); BB != EBB;
178 ++BB) {
179 Instruction *LastTop = getBBStackPtr(BB);
180 for (BasicBlock::iterator Inst = BB->begin(), EInst = BB->end();
181 Inst != EInst; ++Inst) {
182 if (AllocaInst *Alloca = dyn_cast<AllocaInst>(Inst)) {
183 Value *SizeOp = Alloca->getArraySize();
184 unsigned Alignment = Alloca->getAlignment();
185 assert(Alloca->getType() == I8Ptr);
186 assert(SizeOp->getType()->isIntegerTy(32));
187 assert(Alignment <= (1 << 29)); // 512MB
188
189 LastTop = BinaryOperator::CreateSub(LastTop, SizeOp, "", Alloca);
190 if (Alignment > 1)
191 LastTop = BinaryOperator::CreateAnd(LastTop,
192 ConstantInt::get(IntPtrType,
193 -Alignment),
194 "", Alloca);
195 if (MustUpdateStackPtrGlobal)
196 new StoreInst(LastTop, StackPtrVar, Alloca);
197 replaceWithPointer(Alloca, LastTop, DeadInsts);
198 } else if (IntrinsicInst *Intr = dyn_cast<IntrinsicInst>(Inst)) {
199 if (Intr->getIntrinsicID() == Intrinsic::stacksave) {
200 replaceWithPointer(Intr, LastTop, DeadInsts);
201 } else if (Intr->getIntrinsicID() == Intrinsic::stackrestore) {
202 Value *NewStackPtr = Intr->getArgOperand(0);
203 LastTop = new PtrToIntInst(NewStackPtr, IntPtrType, "", Intr);
204 if (MustUpdateStackPtrGlobal)
205 new StoreInst(LastTop, StackPtrVar, Intr);
206 CopyDebug(LastTop, Intr);
207 DeadInsts.push_back(Intr);
208 }
209 } else if (ReturnInst *Return = dyn_cast<ReturnInst>(Inst)) {
210 if (MustUpdateStackPtrGlobal)
211 new StoreInst(InitialStackPtr, StackPtrVar, Return);
212 }
213 }
214
215 // Insert the final frame top value into all successor phi nodes.
216 TerminatorInst *Term = BB->getTerminator();
217 for (unsigned int I = 0; I < Term->getNumSuccessors(); ++I) {
218 PHINode *Succ = cast<PHINode>(getBBStackPtr(Term->getSuccessor(I)));
219 Succ->addIncoming(LastTop, BB);
220 }
221 }
222
223 // Delete the replaced instructions.
224 for (SmallVectorImpl<Instruction *>::const_iterator Inst = DeadInsts.begin(),
225 E = DeadInsts.end(); Inst != E; ++Inst)
226 (*Inst)->eraseFromParent();
227 }
228
229 void ExpandAllocas::insertStackPtrInit(Module &M) {
230 Function *EntryFunction = M.getFunction(minsfi::EntryFunctionName);
231 if (!EntryFunction)
232 report_fatal_error("ExpandAllocas: Module does not have an entry function");
233
234 // Check the signature of the entry function.
235 Function::ArgumentListType &Args = EntryFunction->getArgumentList();
236 if (Args.size() != 1 || Args.front().getType() != IntPtrType) {
237 report_fatal_error(std::string("ExpandAllocas: Invalid signature of ") +
238 minsfi::EntryFunctionName);
239 }
240
241 // Insert a store instruction which saves the value of the first argument
242 // to the stack pointer global variable.
243 new StoreInst(&Args.front(), StackPtrVar,
244 EntryFunction->getEntryBlock().getFirstInsertionPt());
245 }
246
247 char ExpandAllocas::ID = 0;
248 INITIALIZE_PASS(ExpandAllocas, "minsfi-expand-allocas",
249 "Expand allocas to allocate memory on an untrusted stack",
250 false, false)
251
252 ModulePass *llvm::createExpandAllocasPass() {
253 return new ExpandAllocas();
254 }
OLDNEW
« no previous file with comments | « lib/Transforms/MinSFI/CMakeLists.txt ('k') | lib/Transforms/MinSFI/LLVMBuild.txt » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698