OLD | NEW |
(Empty) | |
| 1 //===- SandboxIndirectCalls.cpp - Apply CFI to indirect function calls ----===// |
| 2 // |
| 3 // The LLVM Compiler Infrastructure |
| 4 // |
| 5 // This file is distributed under the University of Illinois Open Source |
| 6 // License. See LICENSE.TXT for details. |
| 7 // |
| 8 //===----------------------------------------------------------------------===// |
| 9 // |
| 10 // This is a pass which applies basic control-flow integrity enforcement to |
| 11 // indirect function calls as a mitigation technique against attempts to |
| 12 // subvert code execution. |
| 13 // |
| 14 // Pointers to address-taken functions are placed into global function tables |
| 15 // (one function table is created per signature) and pointers to functions are |
| 16 // replaced with the respective table indices. Indirect function calls are |
| 17 // rewritten to treat the target pointer as an index and to load the actual |
| 18 // pointer from the corresponding table. |
| 19 // |
| 20 // The zero-index entry of each table is set to null to provide consistent |
| 21 // behaviour for null pointers. Tables are also padded with null entries to |
| 22 // round their size to the nearest power of two and indices passed to calls |
| 23 // are bit-masked accordingly in order to prevent buffer overflow during the |
| 24 // load from the table. |
| 25 // |
| 26 // Even if placed into different tables, two functions are never assigned the |
| 27 // same index. Interpreting a function pointer as a function of an incorrect |
| 28 // signature will therefore result in jumping to null. |
| 29 // |
| 30 // Pointer arithmetic is not allowed on function pointers and will result in |
| 31 // undefined behaviour. |
| 32 // |
| 33 //===----------------------------------------------------------------------===// |
| 34 |
| 35 #include "llvm/Pass.h" |
| 36 #include "llvm/IR/Constants.h" |
| 37 #include "llvm/IR/DataLayout.h" |
| 38 #include "llvm/IR/Function.h" |
| 39 #include "llvm/IR/Module.h" |
| 40 #include "llvm/IR/Instructions.h" |
| 41 #include "llvm/IR/Intrinsics.h" |
| 42 #include "llvm/Support/MathExtras.h" |
| 43 #include "llvm/Transforms/MinSFI.h" |
| 44 #include "llvm/Transforms/NaCl.h" |
| 45 |
| 46 using namespace llvm; |
| 47 |
| 48 static const char InternalSymName_FunctionTable[] = "__sfi_function_table"; |
| 49 |
| 50 namespace { |
| 51 // This pass needs to be a ModulePass because it adds a GlobalVariable. |
| 52 class SandboxIndirectCalls : public ModulePass { |
| 53 public: |
| 54 static char ID; |
| 55 SandboxIndirectCalls() : ModulePass(ID) { |
| 56 initializeSandboxIndirectCallsPass(*PassRegistry::getPassRegistry()); |
| 57 } |
| 58 |
| 59 virtual bool runOnModule(Module &M); |
| 60 }; |
| 61 } // namespace |
| 62 |
| 63 static inline size_t RoundToPowerOf2(size_t n) { |
| 64 if (isPowerOf2_64(n)) |
| 65 return n; |
| 66 else |
| 67 return NextPowerOf2(n); |
| 68 } |
| 69 |
| 70 static inline bool IsPtrToIntUse(const Function::user_iterator &FuncUser) { |
| 71 if (isa<PtrToIntInst>(*FuncUser)) |
| 72 return true; |
| 73 else if (ConstantExpr *Expr = dyn_cast<ConstantExpr>(*FuncUser)) |
| 74 return Expr->getOpcode() == Instruction::PtrToInt; |
| 75 else |
| 76 return false; |
| 77 } |
| 78 |
| 79 // Function use is a direct call if the user is a call instruction and |
| 80 // the function is its last operand. |
| 81 static inline bool IsDirectCallUse(const Function::user_iterator &FuncUser) { |
| 82 if (CallInst *Call = dyn_cast<CallInst>(*FuncUser)) |
| 83 return FuncUser.getOperandNo() == Call->getNumArgOperands(); |
| 84 else |
| 85 return false; |
| 86 } |
| 87 |
| 88 bool SandboxIndirectCalls::runOnModule(Module &M) { |
| 89 typedef SmallVector<Constant*, 16> FunctionVector; |
| 90 DataLayout DL(&M); |
| 91 Type *I32 = Type::getInt32Ty(M.getContext()); |
| 92 Type *IntPtrType = DL.getIntPtrType(M.getContext()); |
| 93 |
| 94 // First, we find all address-taken functions and assign each an index. |
| 95 // Pointers in code are then immediately replaced with these indices, even |
| 96 // though the tables have not been created yet. |
| 97 FunctionVector AddrTakenFuncs; |
| 98 for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) { |
| 99 bool HasIndirectUse = false; |
| 100 Constant *Index = ConstantInt::get(IntPtrType, AddrTakenFuncs.size() + 1); |
| 101 for (Function::user_iterator User = Func->user_begin(), |
| 102 E = Func->user_end(); |
| 103 User != E; ++User) { |
| 104 if (IsPtrToIntUse(User)) { |
| 105 HasIndirectUse = true; |
| 106 (*User)->replaceAllUsesWith(Index); |
| 107 if (Instruction *UserInst = dyn_cast<Instruction>(*User)) |
| 108 UserInst->eraseFromParent(); |
| 109 } else if (!IsDirectCallUse(User)) { |
| 110 report_fatal_error("SandboxIndirectCalls: Invalid reference to " |
| 111 "function @" + Func->getName()); |
| 112 } |
| 113 } |
| 114 |
| 115 if (HasIndirectUse) |
| 116 AddrTakenFuncs.push_back(Func); |
| 117 } |
| 118 |
| 119 // Return if no address-taken functions have been found. |
| 120 if (AddrTakenFuncs.empty()) |
| 121 return false; |
| 122 |
| 123 // Generate and fill out the function tables. Their size is rounded up to the |
| 124 // nearest power of two, index zero is reserved for null and functions are |
| 125 // stored under the indices that were assigned to them earlier. |
| 126 size_t FuncIndex = 1; |
| 127 size_t TableSize = RoundToPowerOf2(AddrTakenFuncs.size() + 1); |
| 128 DenseMap<PointerType*, FunctionVector> TableEntries; |
| 129 for (FunctionVector::iterator It = AddrTakenFuncs.begin(), |
| 130 E = AddrTakenFuncs.end(); It != E; ++It) { |
| 131 Constant *Func = (*It); |
| 132 PointerType *FuncType = cast<PointerType>(Func->getType()); |
| 133 FunctionVector &Table = TableEntries[FuncType]; |
| 134 |
| 135 // If this table has not been initialized yet, fill it with nulls. |
| 136 if (Table.empty()) |
| 137 Table.assign(TableSize, ConstantPointerNull::get(FuncType)); |
| 138 |
| 139 Table[FuncIndex++] = Func; |
| 140 } |
| 141 |
| 142 // Create a global variable for each of the function tables. |
| 143 DenseMap<PointerType*, GlobalVariable*> TableGlobals; |
| 144 for (DenseMap<PointerType*, FunctionVector>::iterator |
| 145 Iter = TableEntries.begin(), E = TableEntries.end(); Iter != E; ++Iter) { |
| 146 PointerType *FuncType = Iter->first; |
| 147 FunctionVector &Table = Iter->second; |
| 148 |
| 149 Constant *TableArray = |
| 150 ConstantArray::get(ArrayType::get(FuncType, TableSize), Table); |
| 151 TableGlobals[FuncType] = |
| 152 new GlobalVariable(M, TableArray->getType(), /*isConstant=*/true, |
| 153 GlobalVariable::InternalLinkage, TableArray, |
| 154 InternalSymName_FunctionTable); |
| 155 } |
| 156 |
| 157 // Iterate over all call instructions and replace integers casted to function |
| 158 // pointers with a load from the corresponding function table (because now |
| 159 // the integers are not pointers but indices). |
| 160 Constant *IndexMask = ConstantInt::get(IntPtrType, TableSize - 1); |
| 161 for (Module::iterator Func = M.begin(), EFunc = M.end(); |
| 162 Func != EFunc; ++Func) { |
| 163 for (Function::iterator BB = Func->begin(), EBB = Func->end(); |
| 164 BB != EBB; ++BB) { |
| 165 for (BasicBlock::iterator Inst = BB->begin(), EInst = BB->end(); |
| 166 Inst != EInst; ++Inst) { |
| 167 if (CallInst *Call = dyn_cast<CallInst>(Inst)) { |
| 168 Value *Callee = Call->getCalledValue(); |
| 169 if (IntToPtrInst *Cast = dyn_cast<IntToPtrInst>(Callee)) { |
| 170 Value *FuncIndex = Cast->getOperand(0); |
| 171 PointerType *FuncType = cast<PointerType>(Cast->getType()); |
| 172 Constant *GlobalVar = TableGlobals[FuncType]; |
| 173 |
| 174 Value *FuncPtr; |
| 175 if (GlobalVar) { |
| 176 Instruction *MaskedIndex = |
| 177 BinaryOperator::CreateAnd(FuncIndex, IndexMask, "", Call); |
| 178 Value *Indexes[] = { ConstantInt::get(I32, 0), MaskedIndex }; |
| 179 Instruction *TableElemPtr = |
| 180 GetElementPtrInst::Create(GlobalVar, Indexes, "", Call); |
| 181 FuncPtr = CopyDebug(new LoadInst(TableElemPtr, "", Call), Cast); |
| 182 } else { |
| 183 // There is no function table for this signature, i.e. the module |
| 184 // does not contain a function which could be called at this site. |
| 185 // We replace the pointer with a null and put a trap in front of |
| 186 // the call because it should never be called. |
| 187 CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap), |
| 188 "", Call); |
| 189 FuncPtr = ConstantPointerNull::get(FuncType); |
| 190 } |
| 191 |
| 192 Call->setCalledFunction(FuncPtr); |
| 193 if (Cast->use_empty()) |
| 194 Cast->eraseFromParent(); |
| 195 } |
| 196 } |
| 197 } |
| 198 } |
| 199 } |
| 200 |
| 201 return true; |
| 202 } |
| 203 |
| 204 char SandboxIndirectCalls::ID = 0; |
| 205 INITIALIZE_PASS(SandboxIndirectCalls, "minsfi-sandbox-indirect-calls", |
| 206 "Add CFI to indirect calls", false, false) |
| 207 |
| 208 ModulePass *llvm::createSandboxIndirectCallsPass() { |
| 209 return new SandboxIndirectCalls(); |
| 210 } |
OLD | NEW |