OLD | NEW |
(Empty) | |
| 1 //===- SandboxMemoryAccesses.cpp - Apply SFI sandboxing to used pointers --===// |
| 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 pass applies SFI sandboxing to all memory access instructions in the IR. |
| 11 // Pointers are truncated to a given number of bits and shifted into a memory |
| 12 // region allocated by the runtime. The runtime reads the pointer bit size |
| 13 // from the "__sfi_pointer_size" exported constant and stores the base of the |
| 14 // correspondingly-sized memory region into the "__sfi_memory_base" global |
| 15 // variable. |
| 16 // |
| 17 // This is meant to be the next to last pass of MinSFI, followed only by a CFI |
| 18 // pass. Because there is no runtime verifier, it must be trusted to correctly |
| 19 // sandbox all dereferenced pointers. |
| 20 // |
| 21 // Sandboxed instructions: |
| 22 // - load, store |
| 23 // - memcpy, memmove, memset |
| 24 // - @llvm.nacl.atomic.load.* |
| 25 // - @llvm.nacl.atomic.store.* |
| 26 // - @llvm.nacl.atomic.rmw.* |
| 27 // - @llvm.nacl.atomic.cmpxchg.* |
| 28 // |
| 29 // Whitelisted instructions: |
| 30 // - ptrtoint |
| 31 // - bitcast |
| 32 // |
| 33 // This pass fails if code contains instructions with pointer-type operands |
| 34 // not listed above. PtrToInt and BitCast instructions are whitelisted because |
| 35 // they do not access memory and therefore do not need to be sandboxed. |
| 36 // |
| 37 // The pass recognizes the pointer arithmetic produced by ExpandGetElementPtr |
| 38 // and reuses its final integer value to save target instructions. This |
| 39 // optimization, as well as the memcpy, memmove and memset intrinsics, is safe |
| 40 // only if the runtime creates a guard region after the dedicated memory region. |
| 41 // The guard region must be the same size as the memory region. |
| 42 // |
| 43 // Both 32-bit and 64-bit architectures are supported. The necessary pointer |
| 44 // arithmetic generated by the pass always uses 64-bit integers. However, when |
| 45 // compiling for 32-bit targets, the backend is expected to optimize the code |
| 46 // by deducing that the top bits are always truncated during the final cast to |
| 47 // a pointer. |
| 48 // |
| 49 // The size of the runtime address subspace can be changed with the |
| 50 // "-minsfi-ptrsize" command-line option. Depending on the target architecture, |
| 51 // the value of this constant can have an effect on the efficiency of the |
| 52 // generated code. On x86-64 and AArch64, 32-bit subspace is the most efficient |
| 53 // because pointers can be sandboxed without bit masking. On AArch32, subspaces |
| 54 // of 24-31 bits will be more efficient because the bit mask fits into a single |
| 55 // BIC instruction immediate. Code for x86 and MIPS is the same for all values. |
| 56 // |
| 57 //===----------------------------------------------------------------------===// |
| 58 |
| 59 #include "llvm/Pass.h" |
| 60 #include "llvm/IR/DataLayout.h" |
| 61 #include "llvm/IR/Function.h" |
| 62 #include "llvm/IR/Instructions.h" |
| 63 #include "llvm/IR/IntrinsicInst.h" |
| 64 #include "llvm/IR/Module.h" |
| 65 #include "llvm/IR/NaClAtomicIntrinsics.h" |
| 66 #include "llvm/Transforms/MinSFI.h" |
| 67 #include "llvm/Transforms/NaCl.h" |
| 68 |
| 69 using namespace llvm; |
| 70 |
| 71 static const char ExternalSymName_MemoryBase[] = "__sfi_memory_base"; |
| 72 static const char ExternalSymName_PointerSize[] = "__sfi_pointer_size"; |
| 73 |
| 74 namespace { |
| 75 // This pass needs to be a ModulePass because it adds a GlobalVariable. |
| 76 class SandboxMemoryAccesses : public ModulePass { |
| 77 Value *MemBaseVar; |
| 78 Value *PtrMask; |
| 79 DataLayout *DL; |
| 80 Type *I32; |
| 81 Type *I64; |
| 82 |
| 83 void sandboxPtrOperand(Instruction *Inst, unsigned int OpNum, |
| 84 bool IsFirstClassValueAccess, Function &Func, |
| 85 Value **MemBase); |
| 86 void sandboxLenOperand(Instruction *Inst, unsigned int OpNum); |
| 87 void checkDoesNotHavePointerOperands(Instruction *Inst); |
| 88 void runOnFunction(Function &Func); |
| 89 |
| 90 public: |
| 91 static char ID; |
| 92 SandboxMemoryAccesses() : ModulePass(ID), MemBaseVar(NULL), PtrMask(NULL), |
| 93 DL(NULL), I32(NULL), I64(NULL) { |
| 94 initializeSandboxMemoryAccessesPass(*PassRegistry::getPassRegistry()); |
| 95 } |
| 96 |
| 97 virtual bool runOnModule(Module &M); |
| 98 }; |
| 99 } // namespace |
| 100 |
| 101 bool SandboxMemoryAccesses::runOnModule(Module &M) { |
| 102 DataLayout Layout(&M); |
| 103 DL = &Layout; |
| 104 I32 = Type::getInt32Ty(M.getContext()); |
| 105 I64 = Type::getInt64Ty(M.getContext()); |
| 106 |
| 107 // Create a global variable with external linkage that will hold the base |
| 108 // address of the sandbox. This variable is defined and initialized by |
| 109 // the runtime. We assume that all original global variables have been |
| 110 // removed during the AllocateDataSegment pass. |
| 111 MemBaseVar = M.getOrInsertGlobal(ExternalSymName_MemoryBase, I64); |
| 112 |
| 113 // Create an exported global constant holding the size of the sandboxed |
| 114 // pointers. If it is smaller than 32 bits, prepare the corresponding bit mask |
| 115 // will later be applied on pointer and length arguments of instructions. |
| 116 unsigned int PointerSize = minsfi::GetPointerSizeInBits(); |
| 117 new GlobalVariable(M, I32, /*isConstant=*/true, |
| 118 GlobalVariable::ExternalLinkage, |
| 119 ConstantInt::get(I32, PointerSize), |
| 120 ExternalSymName_PointerSize); |
| 121 if (PointerSize < 32) |
| 122 PtrMask = ConstantInt::get(I32, (1U << PointerSize) - 1); |
| 123 |
| 124 for (Module::iterator Func = M.begin(), E = M.end(); Func != E; ++Func) |
| 125 runOnFunction(*Func); |
| 126 |
| 127 return true; |
| 128 } |
| 129 |
| 130 void SandboxMemoryAccesses::sandboxPtrOperand(Instruction *Inst, |
| 131 unsigned int OpNum, |
| 132 bool IsFirstClassValueAccess, |
| 133 Function &Func, Value **MemBase) { |
| 134 // Function must first acquire the sandbox memory region base from |
| 135 // the global variable. If this is the first sandboxed pointer, insert |
| 136 // the corresponding load instruction at the beginning of the function. |
| 137 if (!*MemBase) { |
| 138 Instruction *MemBaseInst = new LoadInst(MemBaseVar, "mem_base"); |
| 139 Func.getEntryBlock().getInstList().push_front(MemBaseInst); |
| 140 *MemBase = MemBaseInst; |
| 141 } |
| 142 |
| 143 Value *Ptr = Inst->getOperand(OpNum); |
| 144 Value *Truncated = NULL, *OffsetConst = NULL; |
| 145 |
| 146 // The ExpandGetElementPtr pass replaces the getelementptr instruction |
| 147 // with pointer arithmetic. If we recognize that pointer arithmetic pattern |
| 148 // here, we can sandbox the pointer more efficiently than in the general |
| 149 // case below. |
| 150 // |
| 151 // The recognized pattern is: |
| 152 // %0 = add i32 %x, <const> ; treated as signed, must be >= 0 |
| 153 // %ptr = inttoptr i32 %0 to <type>* |
| 154 // and can be replaced with: |
| 155 // %0 = zext i32 %x to i64 |
| 156 // %1 = add i64 %0, %mem_base |
| 157 // %2 = add i64 %1, <const> ; extended to i64 |
| 158 // %ptr = inttoptr i64 %2 to <type>* |
| 159 // |
| 160 // Since this enables the code to access memory outside the dedicated region, |
| 161 // this is safe only if the memory region is followed by an equally sized |
| 162 // guard region. |
| 163 |
| 164 bool OptimizeGEP = false; |
| 165 Instruction *RedundantCast = NULL, *RedundantAdd = NULL; |
| 166 if (IsFirstClassValueAccess) { |
| 167 if (IntToPtrInst *Cast = dyn_cast<IntToPtrInst>(Ptr)) { |
| 168 if (BinaryOperator *Op = dyn_cast<BinaryOperator>(Cast->getOperand(0))) { |
| 169 if (Op->getOpcode() == Instruction::Add) { |
| 170 if (Op->getType()->isIntegerTy(32)) { |
| 171 if (ConstantInt *CI = dyn_cast<ConstantInt>(Op->getOperand(1))) { |
| 172 Type *ValType = Ptr->getType()->getPointerElementType(); |
| 173 int64_t MaxOffset = minsfi::GetAddressSubspaceSize() - |
| 174 DL->getTypeStoreSize(ValType); |
| 175 int64_t Offset = CI->getSExtValue(); |
| 176 if ((Offset >= 0) && (Offset <= MaxOffset)) { |
| 177 Truncated = Op->getOperand(0); |
| 178 OffsetConst = ConstantInt::get(I64, Offset); |
| 179 RedundantCast = Cast; |
| 180 RedundantAdd = Op; |
| 181 OptimizeGEP = true; |
| 182 } |
| 183 } |
| 184 } |
| 185 } |
| 186 } |
| 187 } |
| 188 } |
| 189 |
| 190 // If the pattern above has not been recognized, start by truncating |
| 191 // the pointer to i32. |
| 192 if (!OptimizeGEP) |
| 193 Truncated = new PtrToIntInst(Ptr, I32, "", Inst); |
| 194 |
| 195 // If the address subspace is smaller than 32 bits, truncate the pointer |
| 196 // further with a bit mask. |
| 197 if (PtrMask) |
| 198 Truncated = BinaryOperator::CreateAnd(Truncated, PtrMask, "", Inst); |
| 199 |
| 200 // Sandbox the pointer by zero-extending it back to 64 bits, and adding |
| 201 // the memory region base. |
| 202 Instruction *Extend = new ZExtInst(Truncated, I64, "", Inst); |
| 203 Instruction *AddBase = BinaryOperator::CreateAdd(*MemBase, Extend, "", Inst); |
| 204 Instruction *AddOffset = |
| 205 OptimizeGEP ? BinaryOperator::CreateAdd(AddBase, OffsetConst, "", Inst) |
| 206 : AddBase; |
| 207 Instruction *SandboxedPtr = |
| 208 new IntToPtrInst(AddOffset, Ptr->getType(), "", Inst); |
| 209 |
| 210 // Replace the pointer in the sandboxed operand |
| 211 Inst->setOperand(OpNum, SandboxedPtr); |
| 212 |
| 213 if (OptimizeGEP) { |
| 214 // Copy debug information |
| 215 CopyDebug(AddOffset, RedundantAdd); |
| 216 CopyDebug(SandboxedPtr, RedundantCast); |
| 217 |
| 218 // Remove instructions if now dead (order matters) |
| 219 if (RedundantCast->use_empty()) |
| 220 RedundantCast->eraseFromParent(); |
| 221 if (RedundantAdd->use_empty()) |
| 222 RedundantAdd->eraseFromParent(); |
| 223 } |
| 224 } |
| 225 |
| 226 void SandboxMemoryAccesses::sandboxLenOperand(Instruction *Inst, |
| 227 unsigned int OpNum) { |
| 228 // Length is assumed to be an i32 value. If the address subspace is smaller, |
| 229 // truncate the value with a bit mask. |
| 230 if (PtrMask) { |
| 231 Value *Len = Inst->getOperand(OpNum); |
| 232 Instruction *MaskedLen = BinaryOperator::CreateAnd(Len, PtrMask, "", Inst); |
| 233 Inst->setOperand(OpNum, MaskedLen); |
| 234 } |
| 235 } |
| 236 |
| 237 void SandboxMemoryAccesses::checkDoesNotHavePointerOperands(Instruction *Inst) { |
| 238 bool hasPointerOperand = false; |
| 239 |
| 240 // Handle Call instructions separately because they always contain |
| 241 // a pointer to the target function. Integrity of calls is guaranteed by CFI. |
| 242 // This pass therefore only checks the function's arguments. |
| 243 if (CallInst *Call = dyn_cast<CallInst>(Inst)) { |
| 244 for (unsigned int I = 0, E = Call->getNumArgOperands(); I < E; ++I) |
| 245 hasPointerOperand |= Call->getArgOperand(I)->getType()->isPointerTy(); |
| 246 } else { |
| 247 for (unsigned int I = 0, E = Inst->getNumOperands(); I < E; ++I) |
| 248 hasPointerOperand |= Inst->getOperand(I)->getType()->isPointerTy(); |
| 249 } |
| 250 |
| 251 if (hasPointerOperand) |
| 252 report_fatal_error("SandboxMemoryAccesses: unexpected instruction with " |
| 253 "pointer-type operands"); |
| 254 } |
| 255 |
| 256 void SandboxMemoryAccesses::runOnFunction(Function &Func) { |
| 257 Value *MemBase = NULL; |
| 258 |
| 259 for (Function::iterator BB = Func.begin(), E = Func.end(); BB != E; ++BB) { |
| 260 for (BasicBlock::iterator Inst = BB->begin(), E = BB->end(); Inst != E; |
| 261 ++Inst) { |
| 262 if (isa<LoadInst>(Inst)) { |
| 263 sandboxPtrOperand(Inst, 0, true, Func, &MemBase); |
| 264 } else if (isa<StoreInst>(Inst)) { |
| 265 sandboxPtrOperand(Inst, 1, true, Func, &MemBase); |
| 266 } else if (isa<MemCpyInst>(Inst) || isa<MemMoveInst>(Inst)) { |
| 267 sandboxPtrOperand(Inst, 0, false, Func, &MemBase); |
| 268 sandboxPtrOperand(Inst, 1, false, Func, &MemBase); |
| 269 sandboxLenOperand(Inst, 2); |
| 270 } else if (isa<MemSetInst>(Inst)) { |
| 271 sandboxPtrOperand(Inst, 0, false, Func, &MemBase); |
| 272 sandboxLenOperand(Inst, 2); |
| 273 } else if (IntrinsicInst *IntrCall = dyn_cast<IntrinsicInst>(Inst)) { |
| 274 switch (IntrCall->getIntrinsicID()) { |
| 275 case Intrinsic::nacl_atomic_load: |
| 276 case Intrinsic::nacl_atomic_cmpxchg: |
| 277 sandboxPtrOperand(IntrCall, 0, true, Func, &MemBase); |
| 278 break; |
| 279 case Intrinsic::nacl_atomic_store: |
| 280 case Intrinsic::nacl_atomic_rmw: |
| 281 case Intrinsic::nacl_atomic_is_lock_free: |
| 282 sandboxPtrOperand(IntrCall, 1, true, Func, &MemBase); |
| 283 break; |
| 284 default: |
| 285 checkDoesNotHavePointerOperands(IntrCall); |
| 286 } |
| 287 } else if (!isa<PtrToIntInst>(Inst) && !isa<BitCastInst>(Inst)) { |
| 288 checkDoesNotHavePointerOperands(Inst); |
| 289 } |
| 290 } |
| 291 } |
| 292 } |
| 293 |
| 294 char SandboxMemoryAccesses::ID = 0; |
| 295 INITIALIZE_PASS(SandboxMemoryAccesses, "minsfi-sandbox-memory-accesses", |
| 296 "Add SFI sandboxing to memory accesses", false, false) |
| 297 |
| 298 ModulePass *llvm::createSandboxMemoryAccessesPass() { |
| 299 return new SandboxMemoryAccesses(); |
| 300 } |
OLD | NEW |