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