| 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();
|
| +}
|
|
|