1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/instrument/transforms/asan_transform.h"
16 :
17 : #include <vector>
18 :
19 : #include "base/logging.h"
20 : #include "base/string_util.h"
21 : #include "base/stringprintf.h"
22 : #include "base/memory/ref_counted.h"
23 : #include "syzygy/block_graph/basic_block_assembler.h"
24 : #include "syzygy/block_graph/block_builder.h"
25 : #include "syzygy/block_graph/block_util.h"
26 : #include "syzygy/common/defs.h"
27 : #include "syzygy/pe/block_util.h"
28 : #include "syzygy/pe/pe_utils.h"
29 : #include "syzygy/pe/transforms/add_imports_transform.h"
30 : #include "third_party/distorm/files/include/mnemonics.h"
31 : #include "third_party/distorm/files/src/x86defs.h"
32 :
33 : namespace instrument {
34 : namespace transforms {
35 : namespace {
36 :
37 : using block_graph::BasicBlock;
38 : using block_graph::BasicCodeBlock;
39 : using block_graph::BasicBlockAssembler;
40 : using block_graph::BasicBlockSubGraph;
41 : using block_graph::BasicBlockReference;
42 : using block_graph::BlockBuilder;
43 : using block_graph::BlockGraph;
44 : using block_graph::Displacement;
45 : using block_graph::Immediate;
46 : using block_graph::Instruction;
47 : using block_graph::Operand;
48 : using block_graph::TypedBlock;
49 : using block_graph::Value;
50 : using block_graph::analysis::LivenessAnalysis;
51 : using block_graph::analysis::MemoryAccessAnalysis;
52 : using core::Register;
53 : using core::RegisterCode;
54 : using pe::transforms::AddImportsTransform;
55 :
56 : // A simple struct that can be used to let us access strings using TypedBlock.
57 : struct StringStruct {
58 : const char string[1];
59 : };
60 :
61 : typedef AddImportsTransform::ImportedModule ImportedModule;
62 : typedef AsanBasicBlockTransform::MemoryAccessMode AsanMemoryAccessMode;
63 : typedef AsanBasicBlockTransform::AsanHookMap HookMap;
64 : typedef std::vector<AsanBasicBlockTransform::AsanHookMapEntryKey>
65 : AccessHookParamVector;
66 : typedef TypedBlock<IMAGE_IMPORT_DESCRIPTOR> ImageImportDescriptor;
67 : typedef TypedBlock<StringStruct> String;
68 :
69 : // Returns true iff opcode should be instrumented.
70 E : bool ShouldInstrumentOpcode(uint16 opcode) {
71 E : switch (opcode) {
72 : // LEA does not actually access memory.
73 : case I_LEA:
74 E : return false;
75 :
76 : // We can ignore the prefetch and clflush instructions. The instrumentation
77 : // will detect memory errors if and when the memory is actually accessed.
78 : case I_CLFLUSH:
79 : case I_PREFETCH:
80 : case I_PREFETCHNTA:
81 : case I_PREFETCHT0:
82 : case I_PREFETCHT1:
83 : case I_PREFETCHT2:
84 : case I_PREFETCHW:
85 i : return false;
86 : }
87 E : return true;
88 E : }
89 :
90 : // Computes the correct displacement, if any, for operand
91 : // number @p operand of @p instr.
92 : Displacement ComputeDisplacementForOperand(const Instruction& instr,
93 E : size_t operand) {
94 E : const _DInst& repr = instr.representation();
95 :
96 : DCHECK(repr.ops[operand].type == O_SMEM ||
97 E : repr.ops[operand].type == O_MEM);
98 :
99 E : size_t access_size_bytes = repr.ops[operand].size / 8;
100 E : if (repr.dispSize == 0)
101 E : return Displacement(access_size_bytes - 1);
102 :
103 E : BasicBlockReference reference;
104 E : if (instr.FindOperandReference(operand, &reference)) {
105 E : if (reference.referred_type() == BasicBlockReference::REFERRED_TYPE_BLOCK) {
106 : return Displacement(reference.block(),
107 E : reference.offset() + access_size_bytes - 1);
108 i : } else {
109 E : return Displacement(reference.basic_block());
110 : }
111 i : } else {
112 E : return Displacement(repr.disp + access_size_bytes - 1);
113 : }
114 E : }
115 :
116 : // Returns true if operand @p op is instrumentable, e.g.
117 : // if it implies a memory access.
118 E : bool IsInstrumentable(const _Operand& op) {
119 E : switch (op.type) {
120 : case O_SMEM:
121 : case O_MEM:
122 E : return true;
123 :
124 : default:
125 E : return false;
126 : }
127 E : }
128 :
129 : // Returns true if opcode @p opcode is a special instruction.
130 : // Memory checks for special instructions (string instructions, instructions
131 : // with prefix, etc) are handled by calling specialized functions rather than
132 : // the standard memory checks.
133 E : bool IsSpecialInstruction(uint16_t opcode) {
134 E : switch (opcode) {
135 : case I_CMPS:
136 : case I_STOS:
137 : case I_MOVS:
138 E : return true;
139 :
140 : default:
141 E : return false;
142 : }
143 E : }
144 :
145 : // Decodes the first O_MEM or O_SMEM operand of @p instr, if any to the
146 : // corresponding Operand.
147 : bool DecodeMemoryAccess(const Instruction& instr,
148 : Operand* access,
149 E : AsanBasicBlockTransform::MemoryAccessInfo* info) {
150 E : DCHECK(access != NULL);
151 E : DCHECK(info != NULL);
152 E : const _DInst& repr = instr.representation();
153 :
154 : // Figure out which operand we're instrumenting.
155 E : size_t mem_op_id = -1;
156 E : if (IsInstrumentable(repr.ops[0]) && IsInstrumentable(repr.ops[1])) {
157 : // This happens with instructions like: MOVS [EDI], [ESI].
158 E : DCHECK(repr.ops[0].size == repr.ops[1].size);
159 E : mem_op_id = 0;
160 E : } else if (IsInstrumentable(repr.ops[0])) {
161 : // The first operand is instrumentable.
162 E : mem_op_id = 0;
163 E : } else if (IsInstrumentable(repr.ops[1])) {
164 : // The second operand is instrumentable.
165 E : mem_op_id = 1;
166 E : } else {
167 : // Neither of the first two operands is instrumentable.
168 E : return false;
169 : }
170 :
171 : // Determine the size of the access.
172 E : info->size = repr.ops[mem_op_id].size / 8;
173 :
174 : // Determine the kind of access (read/write/instr/repz).
175 E : if (FLAG_GET_PREFIX(repr.flags) & FLAG_REPNZ)
176 i : info->mode = AsanBasicBlockTransform::kRepnzAccess;
177 E : else if (FLAG_GET_PREFIX(repr.flags) & FLAG_REP)
178 E : info->mode = AsanBasicBlockTransform::kRepzAccess;
179 E : else if (IsSpecialInstruction(instr.opcode()))
180 E : info->mode = AsanBasicBlockTransform::kInstrAccess;
181 E : else if ((repr.flags & FLAG_DST_WR) && mem_op_id == 0) {
182 : // The first operand is written to.
183 E : info->mode = AsanBasicBlockTransform::kWriteAccess;
184 E : } else {
185 E : info->mode = AsanBasicBlockTransform::kReadAccess;
186 : }
187 :
188 : // Determine the opcode of this instruction (when needed).
189 : if (info->mode == AsanBasicBlockTransform::kRepnzAccess ||
190 : info->mode == AsanBasicBlockTransform::kRepzAccess ||
191 E : info->mode == AsanBasicBlockTransform::kInstrAccess) {
192 E : info->opcode = instr.opcode();
193 : }
194 :
195 : // Determine operand of the access.
196 E : if (repr.ops[mem_op_id].type == O_SMEM) {
197 : // Simple memory dereference with optional displacement.
198 E : Register base_reg(RegisterCode(repr.ops[mem_op_id].index - R_EAX));
199 : // Get the displacement for the operand.
200 E : Displacement displ = ComputeDisplacementForOperand(instr, mem_op_id);
201 :
202 E : *access = Operand(base_reg, displ);
203 E : } else if (repr.ops[0].type == O_MEM || repr.ops[1].type == O_MEM) {
204 : // Complex memory dereference.
205 E : Register index_reg(RegisterCode(repr.ops[mem_op_id].index - R_EAX));
206 E : core::ScaleFactor scale = core::kTimes1;
207 E : switch (repr.scale) {
208 : case 2:
209 E : scale = core::kTimes2;
210 E : break;
211 : case 4:
212 E : scale = core::kTimes4;
213 E : break;
214 : case 8:
215 E : scale = core::kTimes8;
216 : break;
217 : default:
218 : break;
219 : }
220 :
221 : // Get the displacement for the operand (if any).
222 E : Displacement displ = ComputeDisplacementForOperand(instr, mem_op_id);
223 :
224 : // Compute the full operand.
225 E : if (repr.base != R_NONE) {
226 E : Register base_reg(RegisterCode(repr.base - R_EAX));
227 E : if (displ.size() == core::kSizeNone) {
228 : // No displacement, it's a [base + index * scale] access.
229 i : *access = Operand(base_reg, index_reg, scale);
230 i : } else {
231 : // This is a [base + index * scale + displ] access.
232 E : *access = Operand(base_reg, index_reg, scale, displ);
233 : }
234 E : } else {
235 : // No base, this is an [index * scale + displ] access.
236 : // TODO(siggi): AFAIK, there's no encoding for [index * scale] without
237 : // a displacement. If this assert fires, I'm proven wrong.
238 E : DCHECK_NE(core::kSizeNone, displ.size());
239 :
240 E : *access = Operand(index_reg, scale, displ);
241 : }
242 E : } else {
243 i : NOTREACHED();
244 i : return false;
245 : }
246 :
247 E : return true;
248 E : }
249 :
250 : // Use @p bb_asm to inject a hook to @p hook to instrument the access to the
251 : // address stored in the operand @p op.
252 : void InjectAsanHook(BasicBlockAssembler* bb_asm,
253 : const AsanBasicBlockTransform::MemoryAccessInfo& info,
254 : const Operand& op,
255 : BlockGraph::Reference* hook,
256 E : const LivenessAnalysis::State& state) {
257 E : DCHECK(hook != NULL);
258 :
259 : // TODO(etienneb): Use liveness information to implement more efficient hook.
260 :
261 : // Determine which kind of probe to inject.
262 : if (info.mode == AsanBasicBlockTransform::kReadAccess ||
263 E : info.mode == AsanBasicBlockTransform::kWriteAccess) {
264 : // The standard load/store probe assume the address is in EDX.
265 : // It restore the original version of EDX and cleanup the stack.
266 E : bb_asm->push(core::edx);
267 E : bb_asm->lea(core::edx, op);
268 E : bb_asm->call(Operand(Displacement(hook->referenced(), hook->offset())));
269 E : } else {
270 : // The special instruction probe take addresses directly in registers.
271 : // The probe doesn't have any effects on stack, registers and flags.
272 E : bb_asm->call(Operand(Displacement(hook->referenced(), hook->offset())));
273 : }
274 E : }
275 :
276 : typedef std::pair<BlockGraph::Block*, BlockGraph::Offset> ReferenceDest;
277 : typedef std::map<ReferenceDest, ReferenceDest> ReferenceMap;
278 : typedef std::set<BlockGraph::Block*> BlockSet;
279 :
280 : // For every block referencing @p dst_blocks, redirects any reference "ref" in
281 : // @p redirects to @p redirects[ref].
282 : void RedirectReferences(const BlockSet& dst_blocks,
283 E : const ReferenceMap& redirects) {
284 : // For each block referenced by any source reference.
285 E : BlockSet::const_iterator dst_block_it = dst_blocks.begin();
286 E : for (; dst_block_it != dst_blocks.end(); ++dst_block_it) {
287 : // Iterate over all their referrers.
288 E : BlockGraph::Block* referred_block = *dst_block_it;
289 E : BlockGraph::Block::ReferrerSet referrers = referred_block->referrers();
290 E : BlockGraph::Block::ReferrerSet::iterator referrer_it = referrers.begin();
291 E : for (; referrer_it != referrers.end(); ++referrer_it) {
292 E : BlockGraph::Block* referrer = referrer_it->first;
293 :
294 : // Don't redirect references from PE parsed blocks. This actually ends up
295 : // redirecting the IAT entries as well in the worst case.
296 E : if (referrer->attributes() & BlockGraph::PE_PARSED)
297 E : continue;
298 :
299 : // And redirect any references that happen to match a source reference.
300 : BlockGraph::Block::ReferenceMap::const_iterator reference_it =
301 E : referrer->references().begin();
302 :
303 E : for (; reference_it != referrer->references().end(); ++reference_it) {
304 E : const BlockGraph::Reference& ref(reference_it->second);
305 E : ReferenceDest dest(std::make_pair(ref.referenced(), ref.offset()));
306 :
307 E : ReferenceMap::const_iterator it(redirects.find(dest));
308 E : if (it != redirects.end()) {
309 : BlockGraph::Reference new_reference(ref.type(),
310 : ref.size(),
311 : it->second.first,
312 : it->second.second,
313 E : 0);
314 :
315 E : referrer->SetReference(reference_it->first, new_reference);
316 : }
317 E : }
318 E : }
319 E : }
320 E : }
321 :
322 : // Get the name of an asan check access function for an @p access_mode access.
323 : // @param info The memory access information, e.g. the size on a load/store,
324 : // the instruction opcode and the kind of access.
325 : std::string GetAsanCheckAccessFunctionName(
326 E : AsanBasicBlockTransform::MemoryAccessInfo info) {
327 E : DCHECK(info.mode != AsanBasicBlockTransform::kNoAccess);
328 E : DCHECK(info.size != 0);
329 : DCHECK(info.mode == AsanBasicBlockTransform::kReadAccess ||
330 : info.mode == AsanBasicBlockTransform::kWriteAccess ||
331 E : info.opcode != 0);
332 :
333 E : const char* rep_str = NULL;
334 E : if (info.mode == AsanBasicBlockTransform::kRepzAccess)
335 E : rep_str = "_repz";
336 E : else if (info.mode == AsanBasicBlockTransform::kRepnzAccess)
337 i : rep_str = "_repnz";
338 i : else
339 E : rep_str = "";
340 :
341 E : const char* access_mode_str = NULL;
342 E : if (info.mode == AsanBasicBlockTransform::kReadAccess)
343 E : access_mode_str = "read";
344 E : else if (info.mode == AsanBasicBlockTransform::kWriteAccess)
345 E : access_mode_str = "write";
346 E : else
347 E : access_mode_str = reinterpret_cast<char*>(GET_MNEMONIC_NAME(info.opcode));
348 :
349 : std::string function_name =
350 : base::StringPrintf("asan_check%s_%d_byte_%s_access%s",
351 : rep_str,
352 : info.size,
353 : access_mode_str,
354 E : info.save_flags ? "" : "_no_flags");
355 E : StringToLowerASCII(&function_name);
356 E : return function_name.c_str();
357 E : }
358 :
359 : // Add the imports for the asan check access hooks to the block-graph.
360 : // @param hooks_param_vector A vector of hook parameter values.
361 : // @param default_stub_map Stubs for the asan check access functions.
362 : // @param import_module The module for which the import should be added.
363 : // @param check_access_hook_map The map where the reference to the imports
364 : // should be stored.
365 : // @param block_graph The block-graph to populate.
366 : // @param header_block The block containing the module's DOS header of this
367 : // block-graph.
368 : // @returns True on success, false otherwise.
369 : bool AddAsanCheckAccessHooks(
370 : const AccessHookParamVector& hook_param_vector,
371 : const AsanBasicBlockTransform::AsanDefaultHookMap& default_stub_map,
372 : ImportedModule* import_module,
373 : HookMap* check_access_hook_map,
374 : BlockGraph* block_graph,
375 E : BlockGraph::Block* header_block) {
376 E : DCHECK(import_module != NULL);
377 E : DCHECK(check_access_hook_map != NULL);
378 E : DCHECK(block_graph != NULL);
379 E : DCHECK(header_block != NULL);
380 :
381 : // Add the hooks to the import module.
382 :
383 : typedef std::map<AsanBasicBlockTransform::AsanHookMapEntryKey, size_t>
384 : HooksParamsToIdxMap;
385 E : HooksParamsToIdxMap hooks_params_to_idx;
386 :
387 E : AccessHookParamVector::const_iterator iter_params = hook_param_vector.begin();
388 E : for (; iter_params != hook_param_vector.end(); ++iter_params) {
389 : size_t symbol_idx = import_module->AddSymbol(
390 : GetAsanCheckAccessFunctionName(*iter_params),
391 E : ImportedModule::kAlwaysImport);
392 E : hooks_params_to_idx[*iter_params] = symbol_idx;
393 E : }
394 :
395 E : DCHECK_EQ(hooks_params_to_idx.size(), hook_param_vector.size());
396 :
397 : // Transforms the block-graph.
398 :
399 E : AddImportsTransform add_imports_transform;
400 E : add_imports_transform.AddModule(import_module);
401 :
402 E : if (!add_imports_transform.TransformBlockGraph(block_graph, header_block)) {
403 i : LOG(ERROR) << "Unable to add imports for Asan instrumentation DLL.";
404 i : return false;
405 : }
406 :
407 : // Get a reference to each hook and put it in the hooks map.
408 E : HooksParamsToIdxMap::iterator iter_hooks = hooks_params_to_idx.begin();
409 E : for (; iter_hooks != hooks_params_to_idx.end(); ++iter_hooks) {
410 E : BlockGraph::Reference import_reference;
411 : if (!import_module->GetSymbolReference(iter_hooks->second,
412 E : &import_reference)) {
413 i : LOG(ERROR) << "Unable to get import reference for Asan.";
414 i : return false;
415 : }
416 E : HookMap& hook_map = *check_access_hook_map;
417 E : hook_map[iter_hooks->first] = import_reference;
418 :
419 : // In a Chrome sandboxed process the NtMapViewOfSection function is
420 : // intercepted by the sandbox agent. This causes execution in the executable
421 : // before imports have been resolved, as the ntdll patch invokes into the
422 : // executable while resolving imports. As the Asan instrumentation directly
423 : // refers to the IAT entries we need to temporarily stub these function
424 : // until the Asan imports are resolved. To do this we need to make the IAT
425 : // entries for those functions point to a temporarily block and we need to
426 : // mark the image import descriptor for this DLL as bound.
427 : AsanBasicBlockTransform::AsanDefaultHookMap::const_iterator stub_reference =
428 E : default_stub_map.find(iter_hooks->first.mode);
429 E : if (stub_reference == default_stub_map.end()) {
430 i : LOG(ERROR) << "Could not find the default hook for "
431 : << GetAsanCheckAccessFunctionName(iter_hooks->first)
432 : << ".";
433 i : return false;
434 : }
435 :
436 : import_reference.referenced()->SetReference(import_reference.offset(),
437 E : stub_reference->second);
438 E : }
439 :
440 E : return true;
441 E : }
442 :
443 : // Create a stub for the asan_check_access functions. For load/store, the stub
444 : // consists of a small block of code that restores the value of EDX and returns
445 : // to the caller. Otherwise, the stub do return.
446 : // @param block_graph The block-graph to populate with the stub.
447 : // @param stub_name The stub's name.
448 : // @param mode The kind of memory access.
449 : // @param reference Will receive the reference to the created hook.
450 : // @returns true on success, false otherwise.
451 : bool CreateHooksStub(BlockGraph* block_graph,
452 : const base::StringPiece& stub_name,
453 : AsanBasicBlockTransform::MemoryAccessMode mode,
454 E : BlockGraph::Reference* reference) {
455 E : DCHECK(reference != NULL);
456 :
457 : // Find or create the section we put our thunks in.
458 : BlockGraph::Section* thunk_section = block_graph->FindOrAddSection(
459 E : common::kThunkSectionName, pe::kCodeCharacteristics);
460 :
461 E : if (thunk_section == NULL) {
462 i : LOG(ERROR) << "Unable to find or create .thunks section.";
463 i : return false;
464 : }
465 :
466 : std::string stub_name_with_id = base::StringPrintf(
467 E : "%.*s%d", stub_name.length(), stub_name.data(), mode);
468 :
469 : // Create the thunk for standard "load/store" (received address in EDX).
470 E : BasicBlockSubGraph bbsg;
471 : BasicBlockSubGraph::BlockDescription* block_desc = bbsg.AddBlockDescription(
472 E : stub_name_with_id, BlockGraph::CODE_BLOCK, thunk_section->id(), 1, 0);
473 :
474 E : BasicCodeBlock* bb = bbsg.AddBasicCodeBlock(stub_name_with_id);
475 E : block_desc->basic_block_order.push_back(bb);
476 E : BasicBlockAssembler assm(bb->instructions().begin(), &bb->instructions());
477 :
478 : if (mode == AsanBasicBlockTransform::kReadAccess ||
479 E : mode == AsanBasicBlockTransform::kWriteAccess) {
480 : // The thunk body restores the original value of EDX and cleans the stack on
481 : // return.
482 E : assm.mov(core::edx, Operand(core::esp, Displacement(4)));
483 E : assm.ret(4);
484 E : } else {
485 E : assm.ret();
486 : }
487 :
488 : // Condense into a block.
489 E : BlockBuilder block_builder(block_graph);
490 E : if (!block_builder.Merge(&bbsg)) {
491 i : LOG(ERROR) << "Failed to build thunk block.";
492 i : return NULL;
493 : }
494 :
495 : // Exactly one new block should have been created.
496 E : DCHECK_EQ(1u, block_builder.new_blocks().size());
497 E : BlockGraph::Block* thunk = block_builder.new_blocks().front();
498 :
499 E : *reference = BlockGraph::Reference(BlockGraph::ABSOLUTE_REF, 4, thunk, 0, 0);
500 :
501 E : return true;
502 E : }
503 :
504 : } // namespace
505 :
506 : const char AsanBasicBlockTransform::kTransformName[] =
507 : "SyzyAsanBasicBlockTransform";
508 :
509 : bool AsanBasicBlockTransform::InstrumentBasicBlock(
510 E : BasicCodeBlock* basic_block, StackAccessMode stack_mode) {
511 E : DCHECK(basic_block != NULL);
512 :
513 : // Pre-compute liveness information for each instruction.
514 E : std::list<LivenessAnalysis::State> states;
515 E : LivenessAnalysis::State state;
516 E : if (use_liveness_analysis_) {
517 E : liveness_.GetStateAtExitOf(basic_block, &state);
518 :
519 : BasicBlock::Instructions::reverse_iterator rev_iter_inst =
520 E : basic_block->instructions().rbegin();
521 : BasicBlock::Instructions::const_reverse_iterator rev_iter_inst_end =
522 E : basic_block->instructions().rend();
523 E : for (; rev_iter_inst != rev_iter_inst_end; ++rev_iter_inst) {
524 E : const Instruction& instr = *rev_iter_inst;
525 E : liveness_.PropagateBackward(instr, &state);
526 E : states.push_front(state);
527 E : }
528 :
529 E : DCHECK_EQ(states.size(), basic_block->instructions().size());
530 : }
531 :
532 : // Get the memory accesses information for this basic block.
533 E : MemoryAccessAnalysis::State memory_state;
534 E : if (remove_redundant_checks_)
535 E : memory_accesses_.GetStateAtEntryOf(basic_block, &memory_state);
536 :
537 : // Process each instruction and inject a call to Asan when we find an
538 : // instrumentable memory access.
539 : BasicBlock::Instructions::iterator iter_inst =
540 E : basic_block->instructions().begin();
541 E : std::list<LivenessAnalysis::State>::iterator iter_state = states.begin();
542 E : for (; iter_inst != basic_block->instructions().end(); ++iter_inst) {
543 E : Operand operand(core::eax);
544 E : const Instruction& instr = *iter_inst;
545 E : const _DInst& repr = instr.representation();
546 :
547 : MemoryAccessInfo info;
548 E : info.mode = kNoAccess;
549 E : info.size = 0;
550 E : info.opcode = 0;
551 E : info.save_flags = true;
552 :
553 : // Get current instruction liveness information.
554 E : if (use_liveness_analysis_) {
555 E : state = *iter_state;
556 E : ++iter_state;
557 : }
558 :
559 : // When activated, skip redundant memory access check.
560 E : if (remove_redundant_checks_) {
561 E : bool need_memory_access_check = false;
562 E : if (memory_state.HasNonRedundantAccess(instr))
563 E : need_memory_access_check = true;
564 :
565 : // Update the memory accesses information for the current instruction.
566 E : memory_accesses_.PropagateForward(instr, &memory_state);
567 :
568 E : if (!need_memory_access_check)
569 E : continue;
570 : }
571 :
572 : // Insert hook for a standard instruction.
573 E : if (!DecodeMemoryAccess(instr, &operand, &info))
574 E : continue;
575 :
576 : // Bail if this is not a memory access.
577 E : if (info.mode == kNoAccess)
578 i : continue;
579 :
580 : // A basic block reference means that can be either a computed jump,
581 : // or a load from a case table. In either case it doesn't make sense
582 : // to instrument the access.
583 : if (operand.displacement().reference().referred_type() ==
584 E : BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK) {
585 E : continue;
586 : }
587 :
588 : // A block reference means this instruction is reading or writing to
589 : // a global variable or some such. It's viable to pad and align global
590 : // variables and to red-zone the padding, but without that, there's nothing
591 : // to gain by instrumenting these accesses.
592 : if (operand.displacement().reference().referred_type() ==
593 E : BasicBlockReference::REFERRED_TYPE_BLOCK) {
594 E : continue;
595 : }
596 :
597 : // Is this an instruction we should be instrumenting.
598 E : if (!ShouldInstrumentOpcode(repr.opcode))
599 E : continue;
600 :
601 : // If there are no unconventional manipulations of the stack frame, we can
602 : // skip instrumenting stack-based memory access (based on ESP or EBP).
603 : // Conventionally, accesses through ESP/EBP are always on stack.
604 : if (stack_mode == kSafeStackAccess &&
605 : (operand.base() == core::kRegisterEsp ||
606 E : operand.base() == core::kRegisterEbp)) {
607 E : continue;
608 : }
609 :
610 : // We do not instrument memory accesses through special segments.
611 : // FS is used for thread local specifics and GS for CPU info.
612 E : uint8_t segment = SEGMENT_GET(repr.segment);
613 E : if (segment == R_FS || segment == R_GS)
614 E : continue;
615 :
616 : // Finally, don't instrument any filtered instructions.
617 E : if (IsFiltered(*iter_inst))
618 E : continue;
619 :
620 : // Create a BasicBlockAssembler to insert new instruction.
621 E : BasicBlockAssembler bb_asm(iter_inst, &basic_block->instructions());
622 :
623 : // Configure the assembler to copy the SourceRange information of the
624 : // current instrumented instruction into newly created instructions. This is
625 : // a hack to allow valid stack walking and better error reporting, but
626 : // breaks the 1:1 OMAP mapping and may confuse some debuggers.
627 E : if (debug_friendly_)
628 E : bb_asm.set_source_range(instr.source_range());
629 :
630 : if (use_liveness_analysis_ &&
631 E : (info.mode == kReadAccess || info.mode == kWriteAccess)) {
632 : // Use the liveness information to skip saving the flags if possible.
633 E : info.save_flags = state.AreArithmeticFlagsLive();
634 : }
635 :
636 : // Insert hook for standard instructions.
637 E : AsanHookMap::iterator hook = check_access_hooks_->find(info);
638 E : if (hook == check_access_hooks_->end()) {
639 i : LOG(ERROR) << "Invalid access : " << GetAsanCheckAccessFunctionName(info);
640 i : return false;
641 : }
642 :
643 : // Instrument this instruction.
644 E : InjectAsanHook(&bb_asm, info, operand, &hook->second, state);
645 E : }
646 :
647 E : DCHECK(iter_state == states.end());
648 :
649 E : return true;
650 E : }
651 :
652 : bool AsanBasicBlockTransform::TransformBasicBlockSubGraph(
653 E : BlockGraph* block_graph, BasicBlockSubGraph* subgraph) {
654 E : DCHECK(block_graph != NULL);
655 E : DCHECK(subgraph != NULL);
656 :
657 : // Perform a global liveness analysis.
658 E : if (use_liveness_analysis_)
659 E : liveness_.Analyze(subgraph);
660 :
661 : // Perform a redundant memory access analysis.
662 E : if (remove_redundant_checks_)
663 E : memory_accesses_.Analyze(subgraph);
664 :
665 : // Determines if this subgraph uses unconventional stack pointer
666 : // manipulations.
667 E : StackAccessMode stack_mode = kUnsafeStackAccess;
668 E : if (!block_graph::HasUnexpectedStackFrameManipulation(subgraph))
669 E : stack_mode = kSafeStackAccess;
670 :
671 : // Iterates through each basic block and instruments it.
672 : BasicBlockSubGraph::BBCollection::iterator it =
673 E : subgraph->basic_blocks().begin();
674 E : for (; it != subgraph->basic_blocks().end(); ++it) {
675 E : BasicCodeBlock* bb = BasicCodeBlock::Cast(*it);
676 E : if (bb != NULL && !InstrumentBasicBlock(bb, stack_mode))
677 i : return false;
678 E : }
679 E : return true;
680 E : }
681 :
682 : const char AsanTransform::kTransformName[] = "SyzyAsanTransform";
683 :
684 : const char AsanTransform::kAsanHookStubName[] = "asan_hook_stub";
685 :
686 : const char AsanTransform::kSyzyAsanDll[] = "asan_rtl.dll";
687 :
688 : AsanTransform::AsanTransform()
689 : : asan_dll_name_(kSyzyAsanDll),
690 : debug_friendly_(false),
691 : use_liveness_analysis_(false),
692 : remove_redundant_checks_(false),
693 E : check_access_hooks_ref_() {
694 E : }
695 :
696 : bool AsanTransform::PreBlockGraphIteration(BlockGraph* block_graph,
697 E : BlockGraph::Block* header_block) {
698 E : bool already_instrumented = false;
699 : // Ensure that this image has not already been instrumented.
700 E : if (!pe::HasImportEntry(header_block, kSyzyAsanDll, &already_instrumented)) {
701 i : LOG(ERROR) << "Unable to check if the image is already instrumented.";
702 i : return false;
703 : }
704 :
705 E : if (already_instrumented) {
706 i : LOG(ERROR) << "The image is already instrumented.";
707 i : return false;
708 : }
709 :
710 E : AccessHookParamVector access_hook_param_vec;
711 E : AsanBasicBlockTransform::AsanDefaultHookMap default_stub_map;
712 :
713 : // Create the hook stub for read/write instructions.
714 E : BlockGraph::Reference read_write_hook;
715 : if (!CreateHooksStub(block_graph, kAsanHookStubName,
716 : AsanBasicBlockTransform::kReadAccess,
717 E : &read_write_hook)) {
718 i : return false;
719 : }
720 :
721 : // Create the hook stub for strings instructions.
722 E : BlockGraph::Reference instr_hook;
723 : if (!CreateHooksStub(block_graph, kAsanHookStubName,
724 : AsanBasicBlockTransform::kInstrAccess,
725 E : &instr_hook)) {
726 i : return false;
727 : }
728 :
729 : // Map each memory access kind to an appropriate stub.
730 E : default_stub_map[AsanBasicBlockTransform::kReadAccess] = read_write_hook;
731 E : default_stub_map[AsanBasicBlockTransform::kWriteAccess] = read_write_hook;
732 E : default_stub_map[AsanBasicBlockTransform::kInstrAccess] = instr_hook;
733 E : default_stub_map[AsanBasicBlockTransform::kRepzAccess] = instr_hook;
734 E : default_stub_map[AsanBasicBlockTransform::kRepnzAccess] = instr_hook;
735 :
736 : // Add an import entry for the ASAN runtime.
737 E : ImportedModule import_module(asan_dll_name_);
738 :
739 : // Import the hooks for the read/write accesses.
740 E : for (int access_size = 1; access_size <= 32; access_size *= 2) {
741 : MemoryAccessInfo read_info =
742 E : { AsanBasicBlockTransform::kReadAccess, access_size, 0, true };
743 E : access_hook_param_vec.push_back(read_info);
744 E : if (use_liveness_analysis()) {
745 E : read_info.save_flags = false;
746 E : access_hook_param_vec.push_back(read_info);
747 : }
748 :
749 : MemoryAccessInfo write_info =
750 E : { AsanBasicBlockTransform::kWriteAccess, access_size, 0, true };
751 E : access_hook_param_vec.push_back(write_info);
752 E : if (use_liveness_analysis()) {
753 E : write_info.save_flags = false;
754 E : access_hook_param_vec.push_back(write_info);
755 : }
756 E : }
757 :
758 : // Import the hooks for the read/write 10-bytes accesses.
759 : MemoryAccessInfo read_info_10 =
760 E : { AsanBasicBlockTransform::kReadAccess, 10, 0, true };
761 E : access_hook_param_vec.push_back(read_info_10);
762 E : if (use_liveness_analysis()) {
763 E : read_info_10.save_flags = false;
764 E : access_hook_param_vec.push_back(read_info_10);
765 : }
766 :
767 : MemoryAccessInfo write_info_10 =
768 E : { AsanBasicBlockTransform::kWriteAccess, 10, 0, true };
769 E : access_hook_param_vec.push_back(write_info_10);
770 E : if (use_liveness_analysis()) {
771 E : write_info_10.save_flags = false;
772 E : access_hook_param_vec.push_back(write_info_10);
773 : }
774 :
775 : // Import the hooks for strings/prefix memory accesses.
776 E : const _InstructionType strings[] = { I_CMPS, I_MOVS, I_STOS };
777 E : int strings_length = sizeof(strings)/sizeof(_InstructionType);
778 :
779 E : for (int access_size = 1; access_size <= 4; access_size *= 2) {
780 E : for (int inst = 0; inst < strings_length; ++inst) {
781 : MemoryAccessInfo repz_inst_info = {
782 E : AsanBasicBlockTransform::kRepzAccess,
783 E : access_size,
784 E : strings[inst],
785 : true
786 E : };
787 E : access_hook_param_vec.push_back(repz_inst_info);
788 :
789 : MemoryAccessInfo inst_info = {
790 E : AsanBasicBlockTransform::kInstrAccess,
791 E : access_size,
792 E : strings[inst],
793 : true
794 E : };
795 E : access_hook_param_vec.push_back(inst_info);
796 E : }
797 E : }
798 :
799 : if (!AddAsanCheckAccessHooks(access_hook_param_vec,
800 : default_stub_map,
801 : &import_module,
802 : &check_access_hooks_ref_,
803 : block_graph,
804 E : header_block)) {
805 i : return false;
806 : }
807 E : return true;
808 E : }
809 :
810 : bool AsanTransform::OnBlock(BlockGraph* block_graph,
811 E : BlockGraph::Block* block) {
812 E : DCHECK(block_graph != NULL);
813 E : DCHECK(block != NULL);
814 E : if (block->type() != BlockGraph::CODE_BLOCK)
815 E : return true;
816 :
817 E : if (!pe::CodeBlockIsBasicBlockDecomposable(block))
818 E : return true;
819 :
820 : // Use the filter that was passed to us for our child transform.
821 E : AsanBasicBlockTransform transform(&check_access_hooks_ref_);
822 E : transform.set_debug_friendly(debug_friendly());
823 E : transform.set_use_liveness_analysis(use_liveness_analysis());
824 E : transform.set_remove_redundant_checks(remove_redundant_checks());
825 E : transform.set_filter(filter());
826 :
827 E : if (!ApplyBasicBlockSubGraphTransform(&transform, block_graph, block, NULL))
828 i : return false;
829 :
830 E : return true;
831 E : }
832 :
833 : bool AsanTransform::PostBlockGraphIteration(BlockGraph* block_graph,
834 E : BlockGraph::Block* header_block) {
835 : // This function redirects the heap-related kernel32 imports to point to a set
836 : // of "override" imports in the ASAN runtime.
837 :
838 : static const size_t kInvalidIndex = -1;
839 :
840 : struct Kernel32ImportRedirect {
841 : const char* import_name;
842 : const char* redirect_name;
843 : };
844 : static const Kernel32ImportRedirect kKernel32Redirects[] = {
845 : { "HeapCreate", "asan_HeapCreate" },
846 : { "HeapDestroy", "asan_HeapDestroy" },
847 : { "HeapAlloc", "asan_HeapAlloc" },
848 : { "HeapReAlloc", "asan_HeapReAlloc" },
849 : { "HeapFree", "asan_HeapFree" },
850 : { "HeapSize", "asan_HeapSize" },
851 : { "HeapValidate", "asan_HeapValidate" },
852 : { "HeapCompact", "asan_HeapCompact" },
853 : { "HeapLock", "asan_HeapLock" },
854 : { "HeapUnlock", "asan_HeapUnlock" },
855 : { "HeapWalk", "asan_HeapWalk" },
856 : { "HeapSetInformation", "asan_HeapSetInformation" },
857 : { "HeapQueryInformation", "asan_HeapQueryInformation" },
858 : };
859 :
860 : // Initialize the module info for querying kernel32 imports.
861 E : std::vector<std::pair<size_t, size_t>> override_indexes;
862 E : ImportedModule module_kernel32("kernel32.dll");
863 E : for (size_t i = 0; i < arraysize(kKernel32Redirects); ++i) {
864 : size_t kernel32_index =
865 : module_kernel32.AddSymbol(kKernel32Redirects[i].import_name,
866 E : ImportedModule::kFindOnly);
867 E : override_indexes.push_back(std::make_pair(kernel32_index, kInvalidIndex));
868 E : }
869 :
870 : // Query the kernel32 imports.
871 E : AddImportsTransform find_kernel_imports;
872 E : find_kernel_imports.AddModule(&module_kernel32);
873 E : if (!find_kernel_imports.TransformBlockGraph(block_graph, header_block)) {
874 i : LOG(ERROR) << "Unable to find kernel32 imports for redirection.";
875 i : return false;
876 : }
877 :
878 : // Add ASAN imports for those kernel32 functions we found. These will later
879 : // be redirected.
880 E : ImportedModule module_asan(asan_dll_name_);
881 E : for (size_t i = 0; i < arraysize(kKernel32Redirects); ++i) {
882 E : size_t kernel32_index = override_indexes[i].first;
883 E : if (module_kernel32.SymbolIsImported(kernel32_index)) {
884 : size_t asan_index = module_asan.AddSymbol(
885 : kKernel32Redirects[i].redirect_name,
886 E : ImportedModule::kAlwaysImport);
887 E : DCHECK_EQ(kInvalidIndex, override_indexes[i].second);
888 E : override_indexes[i].second = asan_index;
889 : }
890 E : }
891 :
892 : // Another transform can safely be run without invalidating the results
893 : // stored in module_kernel32, as additions to the IAT will strictly be
894 : // performed at the end.
895 E : AddImportsTransform add_imports_transform;
896 E : add_imports_transform.AddModule(&module_asan);
897 E : if (!add_imports_transform.TransformBlockGraph(block_graph, header_block)) {
898 i : LOG(ERROR) << "Unable to add imports for import redirection.";
899 i : return false;
900 : }
901 :
902 : // Keeps track of all the blocks referenced by the original references.
903 E : BlockSet dst_blocks;
904 : // Stores the reference mapping we want to rewrite.
905 E : ReferenceMap reference_redirect_map;
906 :
907 E : for (size_t i = 0; i < override_indexes.size(); ++i) {
908 : // Symbols that aren't imported don't need to be redirected.
909 E : size_t kernel32_index = override_indexes[i].first;
910 E : size_t asan_index = override_indexes[i].second;
911 E : if (!module_kernel32.SymbolIsImported(kernel32_index)) {
912 E : DCHECK_EQ(kInvalidIndex, asan_index);
913 E : continue;
914 : }
915 :
916 E : DCHECK_NE(kInvalidIndex, asan_index);
917 E : BlockGraph::Reference src;
918 E : BlockGraph::Reference dst;
919 : if (!module_kernel32.GetSymbolReference(kernel32_index, &src) ||
920 E : !module_asan.GetSymbolReference(asan_index, &dst)) {
921 i : NOTREACHED() << "Unable to get references after a successful transform.";
922 i : return false;
923 : }
924 :
925 : // Add the destination block to the set of referred blocks.
926 E : dst_blocks.insert(src.referenced());
927 : reference_redirect_map.insert(
928 : std::make_pair(ReferenceDest(src.referenced(), src.offset()),
929 E : ReferenceDest(dst.referenced(), dst.offset())));
930 E : }
931 :
932 E : RedirectReferences(dst_blocks, reference_redirect_map);
933 :
934 : // The timestamp 1 corresponds to Thursday, 01 Jan 1970 00:00:01 GMT. Setting
935 : // the timestamp of the image import descriptor to this value allows us to
936 : // temporarily bind the library until the loader finishes loading this module.
937 : // As the value is far in the past this means that the entries in the IAT for
938 : // this module will all be replace by pointers into the actual library.
939 : static const size_t kDateInThePast = 1;
940 :
941 : // We need to bind the IAT for our module to make sure the stub is used until
942 : // the sandbox lets the loader finish patching the IAT entries.
943 E : module_asan.import_descriptor()->TimeDateStamp = kDateInThePast;
944 :
945 E : return true;
946 E : }
947 :
948 : bool operator<(const AsanBasicBlockTransform::MemoryAccessInfo& left,
949 E : const AsanBasicBlockTransform::MemoryAccessInfo& right) {
950 E : if (left.mode != right.mode)
951 E : return left.mode < right.mode;
952 E : if (left.size != right.size)
953 E : return left.size < right.size;
954 E : if (left.save_flags != right.save_flags)
955 E : return left.save_flags < right.save_flags;
956 E : return left.opcode < right.opcode;
957 E : }
958 :
959 : } // namespace transforms
960 : } // namespace instrument
|