blob: 817e0c29cf1e380ba9bf4aeefa15690421bcbef7 [file] [log] [blame] [edit]
//===----- 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();
}