Index: lib/Transforms/MinSFI/ExpandAllocas.cpp |
diff --git a/lib/Transforms/MinSFI/ExpandAllocas.cpp b/lib/Transforms/MinSFI/ExpandAllocas.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..817e0c29cf1e380ba9bf4aeefa15690421bcbef7 |
--- /dev/null |
+++ b/lib/Transforms/MinSFI/ExpandAllocas.cpp |
@@ -0,0 +1,254 @@ |
+//===----- ExpandAllocas.cpp - Allocate memory on the untrusted stack -----===// |
+// |
+// The LLVM Compiler Infrastructure |
+// |
+// This file is distributed under the University of Illinois Open Source |
+// License. See LICENSE.TXT for details. |
+// |
+//===----------------------------------------------------------------------===// |
+// |
+// Code sandboxed with MinSFI cannot access the execution stack directly |
+// because the stack lies outside of its address subspace, which prevents it |
+// from using memory allocated with the alloca instruction. This pass therefore |
+// replaces allocas with memory allocation on a separate stack at a fixed |
+// location inside the designated memory region. |
+// |
+// The new stack does not have to be trusted as it is only used for memory |
+// allocation inside the sandbox. The call and ret instructions still operate |
+// on the native stack, preventing manipulation with the return address or |
+// callee-saved registers. |
+// |
+// This pass also replaces the @llvm.stacksave and @llvm.stackrestore |
+// intrinsics which would otherwise allow access to the native stack pointer. |
+// Instead, they are expanded out and save/restore the current untrusted stack |
+// pointer. |
+// |
+// When a function is invoked, the current untrusted stack pointer is obtained |
+// from the "__sfi_stack_ptr" global variable (internal to the module). The |
+// function then keeps track of the current value of the stack pointer, but |
+// must update the global variable prior to any function calls and restore the |
+// initial value before it returns. |
+// |
+// The stack pointer is initialized in the entry function of the module, the |
+// _start_minsfi function. The runtime is expected to copy the arguments |
+// (a NULL-terminated integer array) at the end of the allocated memory region, |
+// i.e. at the bottom of the untrusted stack, and pass the pointer to the array |
+// to the entry function. The sandboxed code is then expected to use the |
+// pointer not only to access its arguments but also as the initial value of |
+// its stack pointer and to grow the stack backwards. |
+// |
+// If an alloca requests alignment greater than 1, the untrusted stack pointer |
+// is aligned accordingly. However, the alignment is applied before the address |
+// is sandboxed and therefore the runtime must guarantee that the base address |
+// of the sandbox is aligned to at least 2^29 bytes (=512MB), which is the |
+// maximum alignment supported by LLVM. |
+// |
+// Possible optimizations: |
+// - accumulate constant-sized allocas to reduce the number of stores |
+// into the global stack pointer variable |
+// - remove stores into the global pointer if the respective values never |
+// reach a function call |
+// - align frame to 16 bytes |
+// |
+//===----------------------------------------------------------------------===// |
+ |
+#include "llvm/Pass.h" |
+#include "llvm/IR/Constants.h" |
+#include "llvm/IR/DataLayout.h" |
+#include "llvm/IR/Function.h" |
+#include "llvm/IR/Instructions.h" |
+#include "llvm/IR/IntrinsicInst.h" |
+#include "llvm/IR/Module.h" |
+#include "llvm/Support/MathExtras.h" |
+#include "llvm/Transforms/MinSFI.h" |
+#include "llvm/Transforms/NaCl.h" |
+ |
+using namespace llvm; |
+ |
+static const char InternalSymName_StackPointer[] = "__sfi_stack_ptr"; |
+ |
+namespace { |
+// ExpandAllocas needs to be a ModulePass because it adds a GlobalVariable. |
+class ExpandAllocas : public ModulePass { |
+ GlobalVariable *StackPtrVar; |
+ Type *IntPtrType, *I8Ptr; |
+ |
+ void runOnFunction(Function &Func); |
+ void insertStackPtrInit(Module &M); |
+ |
+public: |
+ static char ID; |
+ ExpandAllocas() : ModulePass(ID), StackPtrVar(NULL), IntPtrType(NULL), |
+ I8Ptr(NULL) { |
+ initializeExpandAllocasPass(*PassRegistry::getPassRegistry()); |
+ } |
+ |
+ virtual bool runOnModule(Module &M); |
+}; |
+} // namespace |
+ |
+bool ExpandAllocas::runOnModule(Module &M) { |
+ DataLayout DL(&M); |
+ IntPtrType = DL.getIntPtrType(M.getContext()); |
+ I8Ptr = Type::getInt8PtrTy(M.getContext()); |
+ |
+ // Create the stack pointer global variable. We are forced to give it some |
+ // initial value, but it will be initialized at runtime. |
+ StackPtrVar = new GlobalVariable(M, IntPtrType, /*isConstant=*/false, |
+ GlobalVariable::InternalLinkage, |
+ ConstantInt::get(IntPtrType, 0), |
+ InternalSymName_StackPointer); |
+ |
+ for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) |
+ runOnFunction(*Func); |
+ |
+ insertStackPtrInit(M); |
+ |
+ return true; |
+} |
+ |
+static inline void replaceWithPointer(Instruction *OrigInst, Value *IntPtr, |
+ SmallVectorImpl<Instruction*> &Dead) { |
+ Instruction *NewInst = |
+ new IntToPtrInst(IntPtr, OrigInst->getType(), "", OrigInst); |
+ NewInst->takeName(OrigInst); |
+ OrigInst->replaceAllUsesWith(NewInst); |
+ CopyDebug(NewInst, OrigInst); |
+ Dead.push_back(OrigInst); |
+} |
+ |
+static inline Instruction *getBBStackPtr(BasicBlock *BB) { |
+ return BB->getInstList().begin(); |
+} |
+ |
+void ExpandAllocas::runOnFunction(Function &Func) { |
+ // Do an initial scan of the entire function body. Check whether it contains |
+ // instructions which we want to operate on the untrusted stack and return |
+ // if there aren't any. Also check whether it contains any function calls. |
+ // If not, we will not have to update the global stack pointer variable. |
+ bool NoUntrustedStackOps = true; |
+ bool MustUpdateStackPtrGlobal = false; |
+ for (Function::iterator BB = Func.begin(), E = Func.end(); BB != E; ++BB) { |
+ for (BasicBlock::iterator Inst = BB->begin(), E = BB->end(); Inst != E; |
+ ++Inst) { |
+ NoUntrustedStackOps &= !isa<AllocaInst>(Inst); |
+ if (CallInst *Call = dyn_cast<CallInst>(Inst)) { |
+ if (isa<IntrinsicInst>(Call)) { |
+ unsigned IntrinsicID = Call->getCalledFunction()->getIntrinsicID(); |
+ bool IsNotStackIntr = IntrinsicID != Intrinsic::stacksave && |
+ IntrinsicID != Intrinsic::stackrestore; |
+ NoUntrustedStackOps &= IsNotStackIntr; |
+ MustUpdateStackPtrGlobal |= IsNotStackIntr; |
+ } else { |
+ MustUpdateStackPtrGlobal = true; |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (NoUntrustedStackOps) |
+ return; |
+ |
+ SmallVector<Instruction *, 10> DeadInsts; |
+ Instruction *InitialStackPtr = new LoadInst(StackPtrVar, "frame_top"); |
+ |
+ // First, we insert a new instruction at the beginning of each basic block, |
+ // which will represent the value of the stack pointer at that point. For |
+ // the entry block, this is the value of the global stack pointer variable. |
+ // Other blocks are initialized with empty phi nodes which we will later |
+ // fill with the values carried over from the respective predecessors. |
+ BasicBlock *EntryBB = &Func.getEntryBlock(); |
+ for (Function::iterator BB = Func.begin(), E = Func.end(); BB != E; ++BB) |
+ BB->getInstList().push_front( |
+ ((BasicBlock*)BB == EntryBB) ? InitialStackPtr |
+ : PHINode::Create(IntPtrType, 2, "")); |
+ |
+ // Now iterate over the instructions and expand out the untrusted stack |
+ // operations. Allocas are replaced with pointer arithmetic that pushes |
+ // the untrusted stack pointer and updates the global stack pointer variable |
+ // if the initial scan identified function calls in the code. |
+ // The @llvm.stacksave intrinsic returns the latest value of the stack |
+ // pointer, and the @llvm.stackrestore overwrites it and potentially updates |
+ // the global variable. If needed, return instructions are prepended with |
+ // a store which restores the initial value of the global variable. |
+ // At the end of each basic block, the last value of the untrusted stack |
+ // pointer is inserted into the phi node at the beginning of each successor |
+ // block. |
+ for (Function::iterator BB = Func.begin(), EBB = Func.end(); BB != EBB; |
+ ++BB) { |
+ Instruction *LastTop = getBBStackPtr(BB); |
+ for (BasicBlock::iterator Inst = BB->begin(), EInst = BB->end(); |
+ Inst != EInst; ++Inst) { |
+ if (AllocaInst *Alloca = dyn_cast<AllocaInst>(Inst)) { |
+ Value *SizeOp = Alloca->getArraySize(); |
+ unsigned Alignment = Alloca->getAlignment(); |
+ assert(Alloca->getType() == I8Ptr); |
+ assert(SizeOp->getType()->isIntegerTy(32)); |
+ assert(Alignment <= (1 << 29)); // 512MB |
+ |
+ LastTop = BinaryOperator::CreateSub(LastTop, SizeOp, "", Alloca); |
+ if (Alignment > 1) |
+ LastTop = BinaryOperator::CreateAnd(LastTop, |
+ ConstantInt::get(IntPtrType, |
+ -Alignment), |
+ "", Alloca); |
+ if (MustUpdateStackPtrGlobal) |
+ new StoreInst(LastTop, StackPtrVar, Alloca); |
+ replaceWithPointer(Alloca, LastTop, DeadInsts); |
+ } else if (IntrinsicInst *Intr = dyn_cast<IntrinsicInst>(Inst)) { |
+ if (Intr->getIntrinsicID() == Intrinsic::stacksave) { |
+ replaceWithPointer(Intr, LastTop, DeadInsts); |
+ } else if (Intr->getIntrinsicID() == Intrinsic::stackrestore) { |
+ Value *NewStackPtr = Intr->getArgOperand(0); |
+ LastTop = new PtrToIntInst(NewStackPtr, IntPtrType, "", Intr); |
+ if (MustUpdateStackPtrGlobal) |
+ new StoreInst(LastTop, StackPtrVar, Intr); |
+ CopyDebug(LastTop, Intr); |
+ DeadInsts.push_back(Intr); |
+ } |
+ } else if (ReturnInst *Return = dyn_cast<ReturnInst>(Inst)) { |
+ if (MustUpdateStackPtrGlobal) |
+ new StoreInst(InitialStackPtr, StackPtrVar, Return); |
+ } |
+ } |
+ |
+ // Insert the final frame top value into all successor phi nodes. |
+ TerminatorInst *Term = BB->getTerminator(); |
+ for (unsigned int I = 0; I < Term->getNumSuccessors(); ++I) { |
+ PHINode *Succ = cast<PHINode>(getBBStackPtr(Term->getSuccessor(I))); |
+ Succ->addIncoming(LastTop, BB); |
+ } |
+ } |
+ |
+ // Delete the replaced instructions. |
+ for (SmallVectorImpl<Instruction *>::const_iterator Inst = DeadInsts.begin(), |
+ E = DeadInsts.end(); Inst != E; ++Inst) |
+ (*Inst)->eraseFromParent(); |
+} |
+ |
+void ExpandAllocas::insertStackPtrInit(Module &M) { |
+ Function *EntryFunction = M.getFunction(minsfi::EntryFunctionName); |
+ if (!EntryFunction) |
+ report_fatal_error("ExpandAllocas: Module does not have an entry function"); |
+ |
+ // Check the signature of the entry function. |
+ Function::ArgumentListType &Args = EntryFunction->getArgumentList(); |
+ if (Args.size() != 1 || Args.front().getType() != IntPtrType) { |
+ report_fatal_error(std::string("ExpandAllocas: Invalid signature of ") + |
+ minsfi::EntryFunctionName); |
+ } |
+ |
+ // Insert a store instruction which saves the value of the first argument |
+ // to the stack pointer global variable. |
+ new StoreInst(&Args.front(), StackPtrVar, |
+ EntryFunction->getEntryBlock().getFirstInsertionPt()); |
+} |
+ |
+char ExpandAllocas::ID = 0; |
+INITIALIZE_PASS(ExpandAllocas, "minsfi-expand-allocas", |
+ "Expand allocas to allocate memory on an untrusted stack", |
+ false, false) |
+ |
+ModulePass *llvm::createExpandAllocasPass() { |
+ return new ExpandAllocas(); |
+} |