| Index: lib/Transforms/MinSFI/SandboxIndirectCalls.cpp
|
| diff --git a/lib/Transforms/MinSFI/SandboxIndirectCalls.cpp b/lib/Transforms/MinSFI/SandboxIndirectCalls.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7ac4b5803a15b85093f121511616043461ca4f63
|
| --- /dev/null
|
| +++ b/lib/Transforms/MinSFI/SandboxIndirectCalls.cpp
|
| @@ -0,0 +1,210 @@
|
| +//===- SandboxIndirectCalls.cpp - Apply CFI to indirect function calls ----===//
|
| +//
|
| +// The LLVM Compiler Infrastructure
|
| +//
|
| +// This file is distributed under the University of Illinois Open Source
|
| +// License. See LICENSE.TXT for details.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +//
|
| +// This is a pass which applies basic control-flow integrity enforcement to
|
| +// indirect function calls as a mitigation technique against attempts to
|
| +// subvert code execution.
|
| +//
|
| +// Pointers to address-taken functions are placed into global function tables
|
| +// (one function table is created per signature) and pointers to functions are
|
| +// replaced with the respective table indices. Indirect function calls are
|
| +// rewritten to treat the target pointer as an index and to load the actual
|
| +// pointer from the corresponding table.
|
| +//
|
| +// The zero-index entry of each table is set to null to provide consistent
|
| +// behaviour for null pointers. Tables are also padded with null entries to
|
| +// round their size to the nearest power of two and indices passed to calls
|
| +// are bit-masked accordingly in order to prevent buffer overflow during the
|
| +// load from the table.
|
| +//
|
| +// Even if placed into different tables, two functions are never assigned the
|
| +// same index. Interpreting a function pointer as a function of an incorrect
|
| +// signature will therefore result in jumping to null.
|
| +//
|
| +// Pointer arithmetic is not allowed on function pointers and will result in
|
| +// undefined behaviour.
|
| +//
|
| +//===----------------------------------------------------------------------===//
|
| +
|
| +#include "llvm/Pass.h"
|
| +#include "llvm/IR/Constants.h"
|
| +#include "llvm/IR/DataLayout.h"
|
| +#include "llvm/IR/Function.h"
|
| +#include "llvm/IR/Module.h"
|
| +#include "llvm/IR/Instructions.h"
|
| +#include "llvm/IR/Intrinsics.h"
|
| +#include "llvm/Support/MathExtras.h"
|
| +#include "llvm/Transforms/MinSFI.h"
|
| +#include "llvm/Transforms/NaCl.h"
|
| +
|
| +using namespace llvm;
|
| +
|
| +static const char InternalSymName_FunctionTable[] = "__sfi_function_table";
|
| +
|
| +namespace {
|
| +// This pass needs to be a ModulePass because it adds a GlobalVariable.
|
| +class SandboxIndirectCalls : public ModulePass {
|
| + public:
|
| + static char ID;
|
| + SandboxIndirectCalls() : ModulePass(ID) {
|
| + initializeSandboxIndirectCallsPass(*PassRegistry::getPassRegistry());
|
| + }
|
| +
|
| + virtual bool runOnModule(Module &M);
|
| +};
|
| +} // namespace
|
| +
|
| +static inline size_t RoundToPowerOf2(size_t n) {
|
| + if (isPowerOf2_64(n))
|
| + return n;
|
| + else
|
| + return NextPowerOf2(n);
|
| +}
|
| +
|
| +static inline bool IsPtrToIntUse(const Function::user_iterator &FuncUser) {
|
| + if (isa<PtrToIntInst>(*FuncUser))
|
| + return true;
|
| + else if (ConstantExpr *Expr = dyn_cast<ConstantExpr>(*FuncUser))
|
| + return Expr->getOpcode() == Instruction::PtrToInt;
|
| + else
|
| + return false;
|
| +}
|
| +
|
| +// Function use is a direct call if the user is a call instruction and
|
| +// the function is its last operand.
|
| +static inline bool IsDirectCallUse(const Function::user_iterator &FuncUser) {
|
| + if (CallInst *Call = dyn_cast<CallInst>(*FuncUser))
|
| + return FuncUser.getOperandNo() == Call->getNumArgOperands();
|
| + else
|
| + return false;
|
| +}
|
| +
|
| +bool SandboxIndirectCalls::runOnModule(Module &M) {
|
| + typedef SmallVector<Constant*, 16> FunctionVector;
|
| + DataLayout DL(&M);
|
| + Type *I32 = Type::getInt32Ty(M.getContext());
|
| + Type *IntPtrType = DL.getIntPtrType(M.getContext());
|
| +
|
| + // First, we find all address-taken functions and assign each an index.
|
| + // Pointers in code are then immediately replaced with these indices, even
|
| + // though the tables have not been created yet.
|
| + FunctionVector AddrTakenFuncs;
|
| + for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) {
|
| + bool HasIndirectUse = false;
|
| + Constant *Index = ConstantInt::get(IntPtrType, AddrTakenFuncs.size() + 1);
|
| + for (Function::user_iterator User = Func->user_begin(),
|
| + E = Func->user_end();
|
| + User != E; ++User) {
|
| + if (IsPtrToIntUse(User)) {
|
| + HasIndirectUse = true;
|
| + (*User)->replaceAllUsesWith(Index);
|
| + if (Instruction *UserInst = dyn_cast<Instruction>(*User))
|
| + UserInst->eraseFromParent();
|
| + } else if (!IsDirectCallUse(User)) {
|
| + report_fatal_error("SandboxIndirectCalls: Invalid reference to "
|
| + "function @" + Func->getName());
|
| + }
|
| + }
|
| +
|
| + if (HasIndirectUse)
|
| + AddrTakenFuncs.push_back(Func);
|
| + }
|
| +
|
| + // Return if no address-taken functions have been found.
|
| + if (AddrTakenFuncs.empty())
|
| + return false;
|
| +
|
| + // Generate and fill out the function tables. Their size is rounded up to the
|
| + // nearest power of two, index zero is reserved for null and functions are
|
| + // stored under the indices that were assigned to them earlier.
|
| + size_t FuncIndex = 1;
|
| + size_t TableSize = RoundToPowerOf2(AddrTakenFuncs.size() + 1);
|
| + DenseMap<PointerType*, FunctionVector> TableEntries;
|
| + for (FunctionVector::iterator It = AddrTakenFuncs.begin(),
|
| + E = AddrTakenFuncs.end(); It != E; ++It) {
|
| + Constant *Func = (*It);
|
| + PointerType *FuncType = cast<PointerType>(Func->getType());
|
| + FunctionVector &Table = TableEntries[FuncType];
|
| +
|
| + // If this table has not been initialized yet, fill it with nulls.
|
| + if (Table.empty())
|
| + Table.assign(TableSize, ConstantPointerNull::get(FuncType));
|
| +
|
| + Table[FuncIndex++] = Func;
|
| + }
|
| +
|
| + // Create a global variable for each of the function tables.
|
| + DenseMap<PointerType*, GlobalVariable*> TableGlobals;
|
| + for (DenseMap<PointerType*, FunctionVector>::iterator
|
| + Iter = TableEntries.begin(), E = TableEntries.end(); Iter != E; ++Iter) {
|
| + PointerType *FuncType = Iter->first;
|
| + FunctionVector &Table = Iter->second;
|
| +
|
| + Constant *TableArray =
|
| + ConstantArray::get(ArrayType::get(FuncType, TableSize), Table);
|
| + TableGlobals[FuncType] =
|
| + new GlobalVariable(M, TableArray->getType(), /*isConstant=*/true,
|
| + GlobalVariable::InternalLinkage, TableArray,
|
| + InternalSymName_FunctionTable);
|
| + }
|
| +
|
| + // Iterate over all call instructions and replace integers casted to function
|
| + // pointers with a load from the corresponding function table (because now
|
| + // the integers are not pointers but indices).
|
| + Constant *IndexMask = ConstantInt::get(IntPtrType, TableSize - 1);
|
| + for (Module::iterator Func = M.begin(), EFunc = M.end();
|
| + Func != EFunc; ++Func) {
|
| + for (Function::iterator BB = Func->begin(), EBB = Func->end();
|
| + BB != EBB; ++BB) {
|
| + for (BasicBlock::iterator Inst = BB->begin(), EInst = BB->end();
|
| + Inst != EInst; ++Inst) {
|
| + if (CallInst *Call = dyn_cast<CallInst>(Inst)) {
|
| + Value *Callee = Call->getCalledValue();
|
| + if (IntToPtrInst *Cast = dyn_cast<IntToPtrInst>(Callee)) {
|
| + Value *FuncIndex = Cast->getOperand(0);
|
| + PointerType *FuncType = cast<PointerType>(Cast->getType());
|
| + Constant *GlobalVar = TableGlobals[FuncType];
|
| +
|
| + Value *FuncPtr;
|
| + if (GlobalVar) {
|
| + Instruction *MaskedIndex =
|
| + BinaryOperator::CreateAnd(FuncIndex, IndexMask, "", Call);
|
| + Value *Indexes[] = { ConstantInt::get(I32, 0), MaskedIndex };
|
| + Instruction *TableElemPtr =
|
| + GetElementPtrInst::Create(GlobalVar, Indexes, "", Call);
|
| + FuncPtr = CopyDebug(new LoadInst(TableElemPtr, "", Call), Cast);
|
| + } else {
|
| + // There is no function table for this signature, i.e. the module
|
| + // does not contain a function which could be called at this site.
|
| + // We replace the pointer with a null and put a trap in front of
|
| + // the call because it should never be called.
|
| + CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap),
|
| + "", Call);
|
| + FuncPtr = ConstantPointerNull::get(FuncType);
|
| + }
|
| +
|
| + Call->setCalledFunction(FuncPtr);
|
| + if (Cast->use_empty())
|
| + Cast->eraseFromParent();
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +char SandboxIndirectCalls::ID = 0;
|
| +INITIALIZE_PASS(SandboxIndirectCalls, "minsfi-sandbox-indirect-calls",
|
| + "Add CFI to indirect calls", false, false)
|
| +
|
| +ModulePass *llvm::createSandboxIndirectCallsPass() {
|
| + return new SandboxIndirectCalls();
|
| +}
|
|
|