| Index: lib/Transforms/MinSFI/SandboxMemoryAccesses.cpp
|
| diff --git a/lib/Transforms/MinSFI/SandboxMemoryAccesses.cpp b/lib/Transforms/MinSFI/SandboxMemoryAccesses.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3da07c7da534d766290bf7b449dede62f60c72ed
|
| --- /dev/null
|
| +++ b/lib/Transforms/MinSFI/SandboxMemoryAccesses.cpp
|
| @@ -0,0 +1,300 @@
|
| +//===- SandboxMemoryAccesses.cpp - Apply SFI sandboxing to used pointers --===//
|
| +//
|
| +// The LLVM Compiler Infrastructure
|
| +//
|
| +// This file is distributed under the University of Illinois Open Source
|
| +// License. See LICENSE.TXT for details.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +//
|
| +// This pass applies SFI sandboxing to all memory access instructions in the IR.
|
| +// Pointers are truncated to a given number of bits and shifted into a memory
|
| +// region allocated by the runtime. The runtime reads the pointer bit size
|
| +// from the "__sfi_pointer_size" exported constant and stores the base of the
|
| +// correspondingly-sized memory region into the "__sfi_memory_base" global
|
| +// variable.
|
| +//
|
| +// This is meant to be the next to last pass of MinSFI, followed only by a CFI
|
| +// pass. Because there is no runtime verifier, it must be trusted to correctly
|
| +// sandbox all dereferenced pointers.
|
| +//
|
| +// Sandboxed instructions:
|
| +// - load, store
|
| +// - memcpy, memmove, memset
|
| +// - @llvm.nacl.atomic.load.*
|
| +// - @llvm.nacl.atomic.store.*
|
| +// - @llvm.nacl.atomic.rmw.*
|
| +// - @llvm.nacl.atomic.cmpxchg.*
|
| +//
|
| +// Whitelisted instructions:
|
| +// - ptrtoint
|
| +// - bitcast
|
| +//
|
| +// This pass fails if code contains instructions with pointer-type operands
|
| +// not listed above. PtrToInt and BitCast instructions are whitelisted because
|
| +// they do not access memory and therefore do not need to be sandboxed.
|
| +//
|
| +// The pass recognizes the pointer arithmetic produced by ExpandGetElementPtr
|
| +// and reuses its final integer value to save target instructions. This
|
| +// optimization, as well as the memcpy, memmove and memset intrinsics, is safe
|
| +// only if the runtime creates a guard region after the dedicated memory region.
|
| +// The guard region must be the same size as the memory region.
|
| +//
|
| +// Both 32-bit and 64-bit architectures are supported. The necessary pointer
|
| +// arithmetic generated by the pass always uses 64-bit integers. However, when
|
| +// compiling for 32-bit targets, the backend is expected to optimize the code
|
| +// by deducing that the top bits are always truncated during the final cast to
|
| +// a pointer.
|
| +//
|
| +// The size of the runtime address subspace can be changed with the
|
| +// "-minsfi-ptrsize" command-line option. Depending on the target architecture,
|
| +// the value of this constant can have an effect on the efficiency of the
|
| +// generated code. On x86-64 and AArch64, 32-bit subspace is the most efficient
|
| +// because pointers can be sandboxed without bit masking. On AArch32, subspaces
|
| +// of 24-31 bits will be more efficient because the bit mask fits into a single
|
| +// BIC instruction immediate. Code for x86 and MIPS is the same for all values.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#include "llvm/Pass.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/IR/NaClAtomicIntrinsics.h"
|
| +#include "llvm/Transforms/MinSFI.h"
|
| +#include "llvm/Transforms/NaCl.h"
|
| +
|
| +using namespace llvm;
|
| +
|
| +static const char ExternalSymName_MemoryBase[] = "__sfi_memory_base";
|
| +static const char ExternalSymName_PointerSize[] = "__sfi_pointer_size";
|
| +
|
| +namespace {
|
| +// This pass needs to be a ModulePass because it adds a GlobalVariable.
|
| +class SandboxMemoryAccesses : public ModulePass {
|
| + Value *MemBaseVar;
|
| + Value *PtrMask;
|
| + DataLayout *DL;
|
| + Type *I32;
|
| + Type *I64;
|
| +
|
| + void sandboxPtrOperand(Instruction *Inst, unsigned int OpNum,
|
| + bool IsFirstClassValueAccess, Function &Func,
|
| + Value **MemBase);
|
| + void sandboxLenOperand(Instruction *Inst, unsigned int OpNum);
|
| + void checkDoesNotHavePointerOperands(Instruction *Inst);
|
| + void runOnFunction(Function &Func);
|
| +
|
| + public:
|
| + static char ID;
|
| + SandboxMemoryAccesses() : ModulePass(ID), MemBaseVar(NULL), PtrMask(NULL),
|
| + DL(NULL), I32(NULL), I64(NULL) {
|
| + initializeSandboxMemoryAccessesPass(*PassRegistry::getPassRegistry());
|
| + }
|
| +
|
| + virtual bool runOnModule(Module &M);
|
| +};
|
| +} // namespace
|
| +
|
| +bool SandboxMemoryAccesses::runOnModule(Module &M) {
|
| + DataLayout Layout(&M);
|
| + DL = &Layout;
|
| + I32 = Type::getInt32Ty(M.getContext());
|
| + I64 = Type::getInt64Ty(M.getContext());
|
| +
|
| + // Create a global variable with external linkage that will hold the base
|
| + // address of the sandbox. This variable is defined and initialized by
|
| + // the runtime. We assume that all original global variables have been
|
| + // removed during the AllocateDataSegment pass.
|
| + MemBaseVar = M.getOrInsertGlobal(ExternalSymName_MemoryBase, I64);
|
| +
|
| + // Create an exported global constant holding the size of the sandboxed
|
| + // pointers. If it is smaller than 32 bits, prepare the corresponding bit mask
|
| + // will later be applied on pointer and length arguments of instructions.
|
| + unsigned int PointerSize = minsfi::GetPointerSizeInBits();
|
| + new GlobalVariable(M, I32, /*isConstant=*/true,
|
| + GlobalVariable::ExternalLinkage,
|
| + ConstantInt::get(I32, PointerSize),
|
| + ExternalSymName_PointerSize);
|
| + if (PointerSize < 32)
|
| + PtrMask = ConstantInt::get(I32, (1U << PointerSize) - 1);
|
| +
|
| + for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func)
|
| + runOnFunction(*Func);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void SandboxMemoryAccesses::sandboxPtrOperand(Instruction *Inst,
|
| + unsigned int OpNum,
|
| + bool IsFirstClassValueAccess,
|
| + Function &Func, Value **MemBase) {
|
| + // Function must first acquire the sandbox memory region base from
|
| + // the global variable. If this is the first sandboxed pointer, insert
|
| + // the corresponding load instruction at the beginning of the function.
|
| + if (!*MemBase) {
|
| + Instruction *MemBaseInst = new LoadInst(MemBaseVar, "mem_base");
|
| + Func.getEntryBlock().getInstList().push_front(MemBaseInst);
|
| + *MemBase = MemBaseInst;
|
| + }
|
| +
|
| + Value *Ptr = Inst->getOperand(OpNum);
|
| + Value *Truncated = NULL, *OffsetConst = NULL;
|
| +
|
| + // The ExpandGetElementPtr pass replaces the getelementptr instruction
|
| + // with pointer arithmetic. If we recognize that pointer arithmetic pattern
|
| + // here, we can sandbox the pointer more efficiently than in the general
|
| + // case below.
|
| + //
|
| + // The recognized pattern is:
|
| + // %0 = add i32 %x, <const> ; treated as signed, must be >= 0
|
| + // %ptr = inttoptr i32 %0 to <type>*
|
| + // and can be replaced with:
|
| + // %0 = zext i32 %x to i64
|
| + // %1 = add i64 %0, %mem_base
|
| + // %2 = add i64 %1, <const> ; extended to i64
|
| + // %ptr = inttoptr i64 %2 to <type>*
|
| + //
|
| + // Since this enables the code to access memory outside the dedicated region,
|
| + // this is safe only if the memory region is followed by an equally sized
|
| + // guard region.
|
| +
|
| + bool OptimizeGEP = false;
|
| + Instruction *RedundantCast = NULL, *RedundantAdd = NULL;
|
| + if (IsFirstClassValueAccess) {
|
| + if (IntToPtrInst *Cast = dyn_cast<IntToPtrInst>(Ptr)) {
|
| + if (BinaryOperator *Op = dyn_cast<BinaryOperator>(Cast->getOperand(0))) {
|
| + if (Op->getOpcode() == Instruction::Add) {
|
| + if (Op->getType()->isIntegerTy(32)) {
|
| + if (ConstantInt *CI = dyn_cast<ConstantInt>(Op->getOperand(1))) {
|
| + Type *ValType = Ptr->getType()->getPointerElementType();
|
| + int64_t MaxOffset = minsfi::GetAddressSubspaceSize() -
|
| + DL->getTypeStoreSize(ValType);
|
| + int64_t Offset = CI->getSExtValue();
|
| + if ((Offset >= 0) && (Offset <= MaxOffset)) {
|
| + Truncated = Op->getOperand(0);
|
| + OffsetConst = ConstantInt::get(I64, Offset);
|
| + RedundantCast = Cast;
|
| + RedundantAdd = Op;
|
| + OptimizeGEP = true;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // If the pattern above has not been recognized, start by truncating
|
| + // the pointer to i32.
|
| + if (!OptimizeGEP)
|
| + Truncated = new PtrToIntInst(Ptr, I32, "", Inst);
|
| +
|
| + // If the address subspace is smaller than 32 bits, truncate the pointer
|
| + // further with a bit mask.
|
| + if (PtrMask)
|
| + Truncated = BinaryOperator::CreateAnd(Truncated, PtrMask, "", Inst);
|
| +
|
| + // Sandbox the pointer by zero-extending it back to 64 bits, and adding
|
| + // the memory region base.
|
| + Instruction *Extend = new ZExtInst(Truncated, I64, "", Inst);
|
| + Instruction *AddBase = BinaryOperator::CreateAdd(*MemBase, Extend, "", Inst);
|
| + Instruction *AddOffset =
|
| + OptimizeGEP ? BinaryOperator::CreateAdd(AddBase, OffsetConst, "", Inst)
|
| + : AddBase;
|
| + Instruction *SandboxedPtr =
|
| + new IntToPtrInst(AddOffset, Ptr->getType(), "", Inst);
|
| +
|
| + // Replace the pointer in the sandboxed operand
|
| + Inst->setOperand(OpNum, SandboxedPtr);
|
| +
|
| + if (OptimizeGEP) {
|
| + // Copy debug information
|
| + CopyDebug(AddOffset, RedundantAdd);
|
| + CopyDebug(SandboxedPtr, RedundantCast);
|
| +
|
| + // Remove instructions if now dead (order matters)
|
| + if (RedundantCast->use_empty())
|
| + RedundantCast->eraseFromParent();
|
| + if (RedundantAdd->use_empty())
|
| + RedundantAdd->eraseFromParent();
|
| + }
|
| +}
|
| +
|
| +void SandboxMemoryAccesses::sandboxLenOperand(Instruction *Inst,
|
| + unsigned int OpNum) {
|
| + // Length is assumed to be an i32 value. If the address subspace is smaller,
|
| + // truncate the value with a bit mask.
|
| + if (PtrMask) {
|
| + Value *Len = Inst->getOperand(OpNum);
|
| + Instruction *MaskedLen = BinaryOperator::CreateAnd(Len, PtrMask, "", Inst);
|
| + Inst->setOperand(OpNum, MaskedLen);
|
| + }
|
| +}
|
| +
|
| +void SandboxMemoryAccesses::checkDoesNotHavePointerOperands(Instruction *Inst) {
|
| + bool hasPointerOperand = false;
|
| +
|
| + // Handle Call instructions separately because they always contain
|
| + // a pointer to the target function. Integrity of calls is guaranteed by CFI.
|
| + // This pass therefore only checks the function's arguments.
|
| + if (CallInst *Call = dyn_cast<CallInst>(Inst)) {
|
| + for (unsigned int I = 0, E = Call->getNumArgOperands(); I < E; ++I)
|
| + hasPointerOperand |= Call->getArgOperand(I)->getType()->isPointerTy();
|
| + } else {
|
| + for (unsigned int I = 0, E = Inst->getNumOperands(); I < E; ++I)
|
| + hasPointerOperand |= Inst->getOperand(I)->getType()->isPointerTy();
|
| + }
|
| +
|
| + if (hasPointerOperand)
|
| + report_fatal_error("SandboxMemoryAccesses: unexpected instruction with "
|
| + "pointer-type operands");
|
| +}
|
| +
|
| +void SandboxMemoryAccesses::runOnFunction(Function &Func) {
|
| + Value *MemBase = NULL;
|
| +
|
| + for (Function::iterator BB = Func.begin(), E = Func.end(); BB != E; ++BB) {
|
| + for (BasicBlock::iterator Inst = BB->begin(), E = BB->end(); Inst != E;
|
| + ++Inst) {
|
| + if (isa<LoadInst>(Inst)) {
|
| + sandboxPtrOperand(Inst, 0, true, Func, &MemBase);
|
| + } else if (isa<StoreInst>(Inst)) {
|
| + sandboxPtrOperand(Inst, 1, true, Func, &MemBase);
|
| + } else if (isa<MemCpyInst>(Inst) || isa<MemMoveInst>(Inst)) {
|
| + sandboxPtrOperand(Inst, 0, false, Func, &MemBase);
|
| + sandboxPtrOperand(Inst, 1, false, Func, &MemBase);
|
| + sandboxLenOperand(Inst, 2);
|
| + } else if (isa<MemSetInst>(Inst)) {
|
| + sandboxPtrOperand(Inst, 0, false, Func, &MemBase);
|
| + sandboxLenOperand(Inst, 2);
|
| + } else if (IntrinsicInst *IntrCall = dyn_cast<IntrinsicInst>(Inst)) {
|
| + switch (IntrCall->getIntrinsicID()) {
|
| + case Intrinsic::nacl_atomic_load:
|
| + case Intrinsic::nacl_atomic_cmpxchg:
|
| + sandboxPtrOperand(IntrCall, 0, true, Func, &MemBase);
|
| + break;
|
| + case Intrinsic::nacl_atomic_store:
|
| + case Intrinsic::nacl_atomic_rmw:
|
| + case Intrinsic::nacl_atomic_is_lock_free:
|
| + sandboxPtrOperand(IntrCall, 1, true, Func, &MemBase);
|
| + break;
|
| + default:
|
| + checkDoesNotHavePointerOperands(IntrCall);
|
| + }
|
| + } else if (!isa<PtrToIntInst>(Inst) && !isa<BitCastInst>(Inst)) {
|
| + checkDoesNotHavePointerOperands(Inst);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +char SandboxMemoryAccesses::ID = 0;
|
| +INITIALIZE_PASS(SandboxMemoryAccesses, "minsfi-sandbox-memory-accesses",
|
| + "Add SFI sandboxing to memory accesses", false, false)
|
| +
|
| +ModulePass *llvm::createSandboxMemoryAccessesPass() {
|
| + return new SandboxMemoryAccesses();
|
| +}
|
|
|