Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(108)

Side by Side Diff: lib/Transforms/MinSFI/SandboxMemoryAccesses.cpp

Issue 939073008: Rebased PNaCl localmods in LLVM to 223109 (Closed)
Patch Set: undo localmod Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/Transforms/MinSFI/SandboxIndirectCalls.cpp ('k') | lib/Transforms/MinSFI/StripTls.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 }
OLDNEW
« no previous file with comments | « lib/Transforms/MinSFI/SandboxIndirectCalls.cpp ('k') | lib/Transforms/MinSFI/StripTls.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698