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 <algorithm>
18 : #include <list>
19 : #include <vector>
20 :
21 : #include "base/logging.h"
22 : #include "base/rand_util.h"
23 : #include "base/memory/ref_counted.h"
24 : #include "base/memory/scoped_vector.h"
25 : #include "base/strings/string_util.h"
26 : #include "base/strings/stringprintf.h"
27 : #include "syzygy/block_graph/basic_block.h"
28 : #include "syzygy/block_graph/basic_block_assembler.h"
29 : #include "syzygy/block_graph/block_builder.h"
30 : #include "syzygy/block_graph/block_util.h"
31 : #include "syzygy/block_graph/typed_block.h"
32 : #include "syzygy/common/defs.h"
33 : #include "syzygy/instrument/transforms/asan_intercepts.h"
34 : #include "syzygy/pe/pe_utils.h"
35 : #include "syzygy/pe/transforms/coff_add_imports_transform.h"
36 : #include "syzygy/pe/transforms/coff_rename_symbols_transform.h"
37 : #include "syzygy/pe/transforms/pe_add_imports_transform.h"
38 : #include "third_party/distorm/files/include/mnemonics.h"
39 : #include "third_party/distorm/files/src/x86defs.h"
40 :
41 : namespace instrument {
42 : namespace transforms {
43 : namespace {
44 :
45 : using block_graph::BasicBlock;
46 : using block_graph::BasicCodeBlock;
47 : using block_graph::BasicBlockAssembler;
48 : using block_graph::BasicBlockSubGraph;
49 : using block_graph::BasicBlockReference;
50 : using block_graph::BlockBuilder;
51 : using block_graph::BlockGraph;
52 : using block_graph::Displacement;
53 : using block_graph::Immediate;
54 : using block_graph::Instruction;
55 : using block_graph::Operand;
56 : using block_graph::TransformPolicyInterface;
57 : using block_graph::TypedBlock;
58 : using block_graph::analysis::LivenessAnalysis;
59 : using block_graph::analysis::MemoryAccessAnalysis;
60 : using assm::Register32;
61 : using pe::transforms::CoffAddImportsTransform;
62 : using pe::transforms::ImportedModule;
63 : using pe::transforms::PEAddImportsTransform;
64 :
65 : // A simple struct that can be used to let us access strings using TypedBlock.
66 : struct StringStruct {
67 : const char string[1];
68 : };
69 :
70 : typedef AsanBasicBlockTransform::MemoryAccessMode AsanMemoryAccessMode;
71 : typedef AsanBasicBlockTransform::AsanHookMap HookMap;
72 : typedef std::vector<AsanBasicBlockTransform::AsanHookMapEntryKey>
73 : AccessHookParamVector;
74 : typedef TypedBlock<IMAGE_IMPORT_DESCRIPTOR> ImageImportDescriptor;
75 : typedef TypedBlock<StringStruct> String;
76 :
77 : // The timestamp 1 corresponds to Thursday, 01 Jan 1970 00:00:01 GMT. Setting
78 : // the timestamp of the image import descriptor to this value allows us to
79 : // temporarily bind the library until the loader finishes loading this module.
80 : // As the value is far in the past this means that the entries in the IAT for
81 : // this module will all be replaced by pointers into the actual library.
82 : // We need to bind the IAT for our module to make sure the stub is used until
83 : // the sandbox lets the loader finish patching the IAT entries.
84 : static const size_t kDateInThePast = 1;
85 :
86 : // Returns true iff opcode should be instrumented.
87 E : bool ShouldInstrumentOpcode(uint16 opcode) {
88 E : switch (opcode) {
89 : // LEA does not actually access memory.
90 : case I_LEA:
91 E : return false;
92 :
93 : // We can ignore the prefetch and clflush instructions. The instrumentation
94 : // will detect memory errors if and when the memory is actually accessed.
95 : case I_CLFLUSH:
96 : case I_PREFETCH:
97 : case I_PREFETCHNTA:
98 : case I_PREFETCHT0:
99 : case I_PREFETCHT1:
100 : case I_PREFETCHT2:
101 : case I_PREFETCHW:
102 i : return false;
103 : }
104 E : return true;
105 E : }
106 :
107 : // Computes the correct displacement, if any, for operand
108 : // number @p operand of @p instr.
109 : BasicBlockAssembler::Displacement ComputeDisplacementForOperand(
110 E : const Instruction& instr, size_t operand) {
111 E : const _DInst& repr = instr.representation();
112 :
113 : DCHECK(repr.ops[operand].type == O_SMEM ||
114 E : repr.ops[operand].type == O_MEM);
115 :
116 E : size_t access_size_bytes = repr.ops[operand].size / 8;
117 E : if (repr.dispSize == 0)
118 E : return Displacement(access_size_bytes - 1);
119 :
120 E : BasicBlockReference reference;
121 E : if (instr.FindOperandReference(operand, &reference)) {
122 E : if (reference.referred_type() == BasicBlockReference::REFERRED_TYPE_BLOCK) {
123 : return Displacement(reference.block(),
124 E : reference.offset() + access_size_bytes - 1);
125 i : } else {
126 E : return Displacement(reference.basic_block());
127 : }
128 i : } else {
129 E : return Displacement(repr.disp + access_size_bytes - 1);
130 : }
131 E : }
132 :
133 : // Returns true if operand @p op is instrumentable, e.g.
134 : // if it implies a memory access.
135 E : bool IsInstrumentable(const _Operand& op) {
136 E : switch (op.type) {
137 : case O_SMEM:
138 : case O_MEM:
139 E : return true;
140 :
141 : default:
142 E : return false;
143 : }
144 E : }
145 :
146 : // Returns true if opcode @p opcode is a special instruction.
147 : // Memory checks for special instructions (string instructions, instructions
148 : // with prefix, etc) are handled by calling specialized functions rather than
149 : // the standard memory checks.
150 E : bool IsSpecialInstruction(uint16_t opcode) {
151 E : switch (opcode) {
152 : case I_CMPS:
153 : case I_STOS:
154 : case I_MOVS:
155 E : return true;
156 :
157 : default:
158 E : return false;
159 : }
160 E : }
161 :
162 : // Decodes the first O_MEM or O_SMEM operand of @p instr, if any to the
163 : // corresponding Operand.
164 : bool DecodeMemoryAccess(const Instruction& instr,
165 : BasicBlockAssembler::Operand* access,
166 E : AsanBasicBlockTransform::MemoryAccessInfo* info) {
167 E : DCHECK(access != NULL);
168 E : DCHECK(info != NULL);
169 E : const _DInst& repr = instr.representation();
170 :
171 : // Figure out which operand we're instrumenting.
172 E : size_t mem_op_id = -1;
173 E : if (IsInstrumentable(repr.ops[0]) && IsInstrumentable(repr.ops[1])) {
174 : // This happens with instructions like: MOVS [EDI], [ESI].
175 E : DCHECK(repr.ops[0].size == repr.ops[1].size);
176 E : mem_op_id = 0;
177 E : } else if (IsInstrumentable(repr.ops[0])) {
178 : // The first operand is instrumentable.
179 E : mem_op_id = 0;
180 E : } else if (IsInstrumentable(repr.ops[1])) {
181 : // The second operand is instrumentable.
182 E : mem_op_id = 1;
183 E : } else {
184 : // Neither of the first two operands is instrumentable.
185 E : return false;
186 : }
187 :
188 : // Determine the size of the access.
189 E : info->size = repr.ops[mem_op_id].size / 8;
190 :
191 : // Determine the kind of access (read/write/instr/repz).
192 E : if (FLAG_GET_PREFIX(repr.flags) & FLAG_REPNZ) {
193 i : info->mode = AsanBasicBlockTransform::kRepnzAccess;
194 E : } else if (FLAG_GET_PREFIX(repr.flags) & FLAG_REP) {
195 E : info->mode = AsanBasicBlockTransform::kRepzAccess;
196 E : } else if (IsSpecialInstruction(instr.opcode())) {
197 E : info->mode = AsanBasicBlockTransform::kInstrAccess;
198 E : } else if ((repr.flags & FLAG_DST_WR) && mem_op_id == 0) {
199 : // The first operand is written to.
200 E : info->mode = AsanBasicBlockTransform::kWriteAccess;
201 E : } else {
202 E : info->mode = AsanBasicBlockTransform::kReadAccess;
203 : }
204 :
205 : // Determine the opcode of this instruction (when needed).
206 : if (info->mode == AsanBasicBlockTransform::kRepnzAccess ||
207 : info->mode == AsanBasicBlockTransform::kRepzAccess ||
208 E : info->mode == AsanBasicBlockTransform::kInstrAccess) {
209 E : info->opcode = instr.opcode();
210 : }
211 :
212 : // Determine operand of the access.
213 E : if (repr.ops[mem_op_id].type == O_SMEM) {
214 : // Simple memory dereference with optional displacement.
215 : const Register32& base_reg = assm::CastAsRegister32(
216 E : core::GetRegister(repr.ops[mem_op_id].index));
217 :
218 : // Get the displacement for the operand.
219 E : auto displ = ComputeDisplacementForOperand(instr, mem_op_id);
220 E : *access = Operand(base_reg, displ);
221 E : } else if (repr.ops[0].type == O_MEM || repr.ops[1].type == O_MEM) {
222 : // Complex memory dereference.
223 : const Register32& index_reg = assm::CastAsRegister32(
224 E : core::GetRegister(repr.ops[mem_op_id].index));
225 :
226 E : assm::ScaleFactor scale = assm::kTimes1;
227 E : switch (repr.scale) {
228 : case 2:
229 E : scale = assm::kTimes2;
230 E : break;
231 : case 4:
232 E : scale = assm::kTimes4;
233 E : break;
234 : case 8:
235 E : scale = assm::kTimes8;
236 : break;
237 : default:
238 : break;
239 : }
240 :
241 : // Get the displacement for the operand (if any).
242 E : auto displ = ComputeDisplacementForOperand(instr, mem_op_id);
243 :
244 : // Compute the full operand.
245 E : if (repr.base != R_NONE) {
246 : const Register32& base_reg = assm::CastAsRegister32(
247 E : core::GetRegister(repr.base));
248 :
249 E : if (displ.size() == assm::kSizeNone) {
250 : // No displacement, it's a [base + index * scale] access.
251 i : *access = Operand(base_reg, index_reg, scale);
252 i : } else {
253 : // This is a [base + index * scale + displ] access.
254 E : *access = Operand(base_reg, index_reg, scale, displ);
255 : }
256 E : } else {
257 : // No base, this is an [index * scale + displ] access.
258 : // TODO(siggi): AFAIK, there's no encoding for [index * scale] without
259 : // a displacement. If this assert fires, I'm proven wrong.
260 E : DCHECK_NE(assm::kSizeNone, displ.size());
261 :
262 E : *access = Operand(index_reg, scale, displ);
263 : }
264 E : } else {
265 i : NOTREACHED();
266 i : return false;
267 : }
268 :
269 E : return true;
270 E : }
271 :
272 : // Use @p bb_asm to inject a hook to @p hook to instrument the access to the
273 : // address stored in the operand @p op.
274 : void InjectAsanHook(BasicBlockAssembler* bb_asm,
275 : const AsanBasicBlockTransform::MemoryAccessInfo& info,
276 : const BasicBlockAssembler::Operand& op,
277 : BlockGraph::Reference* hook,
278 : const LivenessAnalysis::State& state,
279 E : BlockGraph::ImageFormat image_format) {
280 E : DCHECK(hook != NULL);
281 :
282 : // Determine which kind of probe to inject.
283 : // - The standard load/store probe assume the address is in EDX.
284 : // It restore the original version of EDX and cleanup the stack.
285 : // - The special instruction probe take addresses directly in registers.
286 : // The probe doesn't have any effects on stack, registers and flags.
287 : if (info.mode == AsanBasicBlockTransform::kReadAccess ||
288 E : info.mode == AsanBasicBlockTransform::kWriteAccess) {
289 : // Load/store probe.
290 E : bb_asm->push(assm::edx);
291 E : bb_asm->lea(assm::edx, op);
292 : }
293 :
294 : // Call the hook.
295 E : if (image_format == BlockGraph::PE_IMAGE) {
296 : // In PE images the hooks are brought in as imports, so they are indirect
297 : // references.
298 E : bb_asm->call(Operand(Displacement(hook->referenced(), hook->offset())));
299 E : } else {
300 E : DCHECK_EQ(BlockGraph::COFF_IMAGE, image_format);
301 : // In COFF images the hooks are brought in as symbols, so they are direct
302 : // references.
303 E : bb_asm->call(Immediate(hook->referenced(), hook->offset()));
304 : }
305 E : }
306 :
307 : // Get the name of an asan check access function for an @p access_mode access.
308 : // @param info The memory access information, e.g. the size on a load/store,
309 : // the instruction opcode and the kind of access.
310 : std::string GetAsanCheckAccessFunctionName(
311 : AsanBasicBlockTransform::MemoryAccessInfo info,
312 E : BlockGraph::ImageFormat image_format) {
313 E : DCHECK(info.mode != AsanBasicBlockTransform::kNoAccess);
314 E : DCHECK_NE(0U, info.size);
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 : // For COFF images we use the decorated function name, which contains a
336 : // leading underscore.
337 : std::string function_name =
338 : base::StringPrintf("%sasan_check%s_%d_byte_%s_access%s",
339 : image_format == BlockGraph::PE_IMAGE ? "" : "_",
340 : rep_str,
341 : info.size,
342 : access_mode_str,
343 E : info.save_flags ? "" : "_no_flags");
344 E : StringToLowerASCII(&function_name);
345 E : return function_name;
346 E : }
347 :
348 : // Add imports from the specified module to the block graph, altering the
349 : // contents of its header/special blocks.
350 : // @param policy the policy object restricting how the transform is applied.
351 : // @param block_graph the block graph to modify.
352 : // @param header_block the header block of @p block_graph.
353 : // @param module the module to import, with its symbols.
354 : // @returns true on success, or false on failure.
355 : bool AddImportsFromModule(const TransformPolicyInterface* policy,
356 : BlockGraph* block_graph,
357 : BlockGraph::Block* header_block,
358 E : ImportedModule* module) {
359 E : if (block_graph->image_format() == BlockGraph::PE_IMAGE) {
360 E : PEAddImportsTransform transform;
361 E : transform.AddModule(module);
362 : if (!ApplyBlockGraphTransform(&transform, policy,
363 E : block_graph, header_block)) {
364 i : return false;
365 : }
366 E : } else {
367 E : DCHECK_EQ(BlockGraph::COFF_IMAGE, block_graph->image_format());
368 E : CoffAddImportsTransform transform;
369 E : transform.AddModule(module);
370 : if (!ApplyBlockGraphTransform(&transform, policy,
371 E : block_graph, header_block)) {
372 i : return false;
373 : }
374 E : }
375 :
376 E : return true;
377 E : }
378 :
379 : // Add the imports for the asan check access hooks to the block-graph.
380 : // @param hooks_param_vector A vector of hook parameter values.
381 : // @param default_stub_map Stubs for the asan check access functions.
382 : // @param import_module The module for which the import should be added.
383 : // @param check_access_hook_map The map where the reference to the imports
384 : // should be stored.
385 : // @param policy The policy object restricting how the transform is applied.
386 : // @param block_graph The block-graph to populate.
387 : // @param header_block The block containing the module's DOS header of this
388 : // block-graph.
389 : // @returns True on success, false otherwise.
390 : bool AddAsanCheckAccessHooks(
391 : const AccessHookParamVector& hook_param_vector,
392 : const AsanBasicBlockTransform::AsanDefaultHookMap& default_stub_map,
393 : ImportedModule* import_module,
394 : HookMap* check_access_hook_map,
395 : const TransformPolicyInterface* policy,
396 : BlockGraph* block_graph,
397 E : BlockGraph::Block* header_block) {
398 E : DCHECK(import_module != NULL);
399 E : DCHECK(check_access_hook_map != NULL);
400 E : DCHECK(policy != NULL);
401 E : DCHECK(block_graph != NULL);
402 E : DCHECK(header_block != NULL);
403 :
404 : typedef std::map<AsanBasicBlockTransform::AsanHookMapEntryKey, size_t>
405 : HooksParamsToIdxMap;
406 E : HooksParamsToIdxMap hooks_params_to_idx;
407 :
408 : // Add the hooks to the import module.
409 E : AccessHookParamVector::const_iterator iter_params = hook_param_vector.begin();
410 E : for (; iter_params != hook_param_vector.end(); ++iter_params) {
411 : size_t symbol_idx = import_module->AddSymbol(
412 : GetAsanCheckAccessFunctionName(*iter_params,
413 : block_graph->image_format()),
414 E : ImportedModule::kAlwaysImport);
415 E : hooks_params_to_idx[*iter_params] = symbol_idx;
416 E : }
417 :
418 E : DCHECK_EQ(hooks_params_to_idx.size(), hook_param_vector.size());
419 :
420 : // Add the imports. This takes care of invoking the appropriate format
421 : // specific transform.
422 E : if (!AddImportsFromModule(policy, block_graph, header_block, import_module)) {
423 i : LOG(ERROR) << "Unable to add imports for Asan instrumentation DLL.";
424 i : return false;
425 : }
426 :
427 : // Get a reference to each hook and put it in the hooks map.
428 E : HooksParamsToIdxMap::iterator iter_hooks = hooks_params_to_idx.begin();
429 E : for (; iter_hooks != hooks_params_to_idx.end(); ++iter_hooks) {
430 E : BlockGraph::Reference import_reference;
431 : if (!import_module->GetSymbolReference(iter_hooks->second,
432 E : &import_reference)) {
433 i : LOG(ERROR) << "Unable to get import reference for Asan.";
434 i : return false;
435 : }
436 E : HookMap& hook_map = *check_access_hook_map;
437 E : hook_map[iter_hooks->first] = import_reference;
438 :
439 : // We only need dummy implementation stubs for PE images, as the hooks are
440 : // imported. COFF instrumented images contain the hooks directly.
441 E : if (block_graph->image_format() == BlockGraph::PE_IMAGE) {
442 : // In a Chrome sandboxed process the NtMapViewOfSection function is
443 : // intercepted by the sandbox agent. This causes execution in the
444 : // executable before imports have been resolved, as the ntdll patch
445 : // invokes into the executable while resolving imports. As the Asan
446 : // instrumentation directly refers to the IAT entries we need to
447 : // temporarily stub these function until the Asan imports are resolved. To
448 : // do this we need to make the IAT entries for those functions point to a
449 : // temporarily block and we need to mark the image import descriptor for
450 : // this DLL as bound.
451 : AsanBasicBlockTransform::AsanDefaultHookMap::const_iterator
452 E : stub_reference = default_stub_map.find(iter_hooks->first.mode);
453 E : if (stub_reference == default_stub_map.end()) {
454 i : LOG(ERROR) << "Could not find the default hook for "
455 : << GetAsanCheckAccessFunctionName(iter_hooks->first,
456 : BlockGraph::PE_IMAGE)
457 : << ".";
458 i : return false;
459 : }
460 :
461 : import_reference.referenced()->SetReference(import_reference.offset(),
462 E : stub_reference->second);
463 : }
464 E : }
465 :
466 E : return true;
467 E : }
468 :
469 : // Create a stub for the asan_check_access functions. For load/store, the stub
470 : // consists of a small block of code that restores the value of EDX and returns
471 : // to the caller. Otherwise, the stub do return.
472 : // @param block_graph The block-graph to populate with the stub.
473 : // @param stub_name The stub's name.
474 : // @param mode The kind of memory access.
475 : // @param reference Will receive the reference to the created hook.
476 : // @returns true on success, false otherwise.
477 : bool CreateHooksStub(BlockGraph* block_graph,
478 : const base::StringPiece& stub_name,
479 : AsanBasicBlockTransform::MemoryAccessMode mode,
480 E : BlockGraph::Reference* reference) {
481 E : DCHECK(reference != NULL);
482 :
483 : // Find or create the section we put our thunks in.
484 : BlockGraph::Section* thunk_section = block_graph->FindOrAddSection(
485 E : common::kThunkSectionName, pe::kCodeCharacteristics);
486 :
487 E : if (thunk_section == NULL) {
488 i : LOG(ERROR) << "Unable to find or create .thunks section.";
489 i : return false;
490 : }
491 :
492 : std::string stub_name_with_id = base::StringPrintf(
493 E : "%.*s%d", stub_name.length(), stub_name.data(), mode);
494 :
495 : // Create the thunk for standard "load/store" (received address in EDX).
496 E : BasicBlockSubGraph bbsg;
497 : BasicBlockSubGraph::BlockDescription* block_desc = bbsg.AddBlockDescription(
498 : stub_name_with_id,
499 : thunk_section->name(),
500 : BlockGraph::CODE_BLOCK,
501 : thunk_section->id(),
502 : 1,
503 E : 0);
504 :
505 E : BasicCodeBlock* bb = bbsg.AddBasicCodeBlock(stub_name_with_id);
506 E : block_desc->basic_block_order.push_back(bb);
507 E : BasicBlockAssembler assm(bb->instructions().begin(), &bb->instructions());
508 :
509 : if (mode == AsanBasicBlockTransform::kReadAccess ||
510 E : mode == AsanBasicBlockTransform::kWriteAccess) {
511 : // The thunk body restores the original value of EDX and cleans the stack on
512 : // return.
513 E : assm.mov(assm::edx, Operand(assm::esp, Displacement(4)));
514 E : assm.ret(4);
515 E : } else {
516 E : assm.ret();
517 : }
518 :
519 : // Condense into a block.
520 E : BlockBuilder block_builder(block_graph);
521 E : if (!block_builder.Merge(&bbsg)) {
522 i : LOG(ERROR) << "Failed to build thunk block.";
523 i : return NULL;
524 : }
525 :
526 : // Exactly one new block should have been created.
527 E : DCHECK_EQ(1u, block_builder.new_blocks().size());
528 E : BlockGraph::Block* thunk = block_builder.new_blocks().front();
529 :
530 E : *reference = BlockGraph::Reference(BlockGraph::ABSOLUTE_REF, 4, thunk, 0, 0);
531 :
532 E : return true;
533 E : }
534 :
535 : // Since MSVS 2012 the implementation of the CRT _heap_init function has changed
536 : // and as a result the CRT defers all its allocation to the process heap.
537 : //
538 : // As we don't want to replace the process heap by an Asan heap we need to patch
539 : // this function to make it use ::HeapCreate instead of ::GetProcessHeap. We
540 : // can't simply intercept it like we do for the other functions because we need
541 : // to update the value of the _crtheap variable, adding an export for it will
542 : // make the instrumentation agent depend on the instrumented image.
543 : //
544 : // Here's the assembly code that we're injecting to replace this function.
545 : //
546 : // push 0
547 : // push 1000h
548 : // push 0
549 : // call syzyasan_rtl!asan_HeapCreate
550 : // mov dword ptr [module!crt_heap], eax
551 : // mov eax,1
552 : // ret
553 : //
554 : // TODO(sebmarchand): Also patch the _heap_term function. This function isn't
555 : // always present and is just used to reset the crt_heap pointer and free
556 : // the underlying heap. This isn't so important in this case because it only
557 : // happen when the process terminate and the heap will be automatically
558 : // freed when we unload the SyzyAsan agent DLL.
559 : //
560 : // @param block_graph The block-graph to populate with the stub.
561 : // @param header_block the header block of @p block_graph.
562 : // @param policy the policy object restricting how the transform is applied.
563 : // @returns true on success, false otherwise.
564 : bool PatchCRTHeapInitialization(BlockGraph* block_graph,
565 : BlockGraph::Block* header_block,
566 E : const TransformPolicyInterface* policy) {
567 E : DCHECK_NE(static_cast<BlockGraph*>(nullptr), block_graph);
568 E : DCHECK_NE(static_cast<BlockGraph::Block*>(nullptr), header_block);
569 E : DCHECK_NE(static_cast<const TransformPolicyInterface*>(nullptr), policy);
570 :
571 : // The original _heap_init block.
572 E : BlockGraph::Block* heap_init_block = nullptr;
573 : // The data block to _crtheap.
574 E : BlockGraph::Block* crtheap_block = nullptr;
575 :
576 E : for (auto& iter : block_graph->blocks_mutable()) {
577 E : if (::strcmp(iter.second.name().c_str(), "_heap_init") == 0) {
578 E : DCHECK_EQ(static_cast<BlockGraph::Block*>(nullptr), heap_init_block);
579 E : heap_init_block = &(iter.second);
580 E : } else if (::strcmp(iter.second.name().c_str(), "_crtheap") == 0) {
581 E : DCHECK_EQ(static_cast<BlockGraph::Block*>(nullptr), crtheap_block);
582 E : crtheap_block = &(iter.second);
583 : }
584 :
585 E : if (heap_init_block != nullptr && crtheap_block != nullptr)
586 E : break;
587 E : }
588 :
589 E : if (heap_init_block == nullptr || crtheap_block == nullptr)
590 E : return true;
591 :
592 : // Find or create the section we put our thunks in.
593 : BlockGraph::Section* thunk_section = block_graph->FindOrAddSection(
594 E : common::kThunkSectionName, pe::kCodeCharacteristics);
595 :
596 E : if (thunk_section == NULL) {
597 i : LOG(ERROR) << "Unable to find or create .thunks section.";
598 i : return false;
599 : }
600 :
601 : // Find the asan_HeapCreate import.
602 E : PEAddImportsTransform find_imports;
603 E : ImportedModule kernel32_module(AsanTransform::kSyzyAsanDll);
604 E : kernel32_module.AddSymbol("asan_HeapCreate", ImportedModule::kAlwaysImport);
605 E : find_imports.AddModule(&kernel32_module);
606 E : if (!find_imports.TransformBlockGraph(policy, block_graph, header_block)) {
607 i : LOG(ERROR) << "Unable to find the asan_HeapCreate import.";
608 i : return false;
609 : }
610 E : BlockGraph::Reference heap_create_ref;
611 E : CHECK(kernel32_module.GetSymbolReference(0, &heap_create_ref));
612 :
613 E : std::string stub_name = "asan_heap_init";
614 E : BasicBlockSubGraph bbsg;
615 : BasicBlockSubGraph::BlockDescription* block_desc = bbsg.AddBlockDescription(
616 : stub_name,
617 : thunk_section->name(),
618 : BlockGraph::CODE_BLOCK,
619 : thunk_section->id(),
620 : 1,
621 E : 0);
622 :
623 E : BasicCodeBlock* bb = bbsg.AddBasicCodeBlock(stub_name);
624 E : block_desc->basic_block_order.push_back(bb);
625 E : BasicBlockAssembler assm(bb->instructions().begin(), &bb->instructions());
626 E : assm.push(Immediate(0U, assm::kSize32Bit));
627 E : assm.push(Immediate(0x1000U, assm::kSize32Bit));
628 E : assm.push(Immediate(0U, assm::kSize32Bit));
629 : assm.call(Operand(Displacement(heap_create_ref.referenced(),
630 E : heap_create_ref.offset())));
631 E : assm.mov(Operand(Displacement(crtheap_block, 0U)), assm::eax);
632 E : assm.mov(assm::eax, Immediate(1U));
633 E : assm.ret();
634 :
635 : // Condense into a block.
636 E : BlockBuilder block_builder(block_graph);
637 E : if (!block_builder.Merge(&bbsg)) {
638 i : LOG(ERROR) << "Failed to build thunk block.";
639 i : return NULL;
640 : }
641 :
642 : // Exactly one new block should have been created.
643 E : DCHECK_EQ(1u, block_builder.new_blocks().size());
644 E : BlockGraph::Block* heap_init_thunk = block_builder.new_blocks().front();
645 :
646 : heap_init_block->TransferReferrers(0U, heap_init_thunk,
647 E : BlockGraph::Block::kTransferInternalReferences);
648 :
649 : if (!heap_init_block->RemoveAllReferences() ||
650 E : !block_graph->RemoveBlock(heap_init_block)) {
651 i : LOG(ERROR) << "Unable to remove the original _heap_init block.";
652 i : return false;
653 : }
654 :
655 E : return true;
656 E : }
657 :
658 : typedef std::map<std::string, size_t> ImportNameIndexMap;
659 :
660 : bool PeFindImportsToIntercept(bool use_interceptors,
661 : const AsanIntercept* intercepts,
662 : const TransformPolicyInterface* policy,
663 : BlockGraph* block_graph,
664 : BlockGraph::Block* header_block,
665 : ScopedVector<ImportedModule>* imported_modules,
666 : ImportNameIndexMap* import_name_index_map,
667 E : ImportedModule* asan_rtl) {
668 E : DCHECK_NE(reinterpret_cast<AsanIntercept*>(NULL), intercepts);
669 E : DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
670 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
671 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), header_block);
672 : DCHECK_NE(reinterpret_cast<ScopedVector<ImportedModule>*>(NULL),
673 E : imported_modules);
674 E : DCHECK_NE(reinterpret_cast<ImportNameIndexMap*>(NULL), import_name_index_map);
675 E : DCHECK_NE(reinterpret_cast<ImportedModule*>(NULL), asan_rtl);
676 :
677 : // Process all of the import intercepts.
678 E : PEAddImportsTransform find_imports;
679 E : ImportedModule* current_module = NULL;
680 E : const char* current_module_name = NULL;
681 E : const AsanIntercept* intercept = intercepts;
682 E : for (; intercept->undecorated_name != NULL; ++intercept) {
683 : // Create a new module to house these imports.
684 E : if (intercept->module != current_module_name) {
685 E : current_module_name = intercept->module;
686 E : current_module = NULL;
687 E : if (current_module_name) {
688 E : current_module = new ImportedModule(current_module_name);
689 E : imported_modules->push_back(current_module);
690 E : find_imports.AddModule(current_module);
691 : }
692 : }
693 :
694 : // If no module name is specified then this interception is not an import
695 : // interception.
696 E : if (current_module_name == NULL)
697 E : continue;
698 :
699 : // Don't process optional intercepts unless asked to.
700 E : if (!use_interceptors && intercept->optional)
701 E : continue;
702 :
703 : current_module->AddSymbol(intercept->undecorated_name,
704 E : ImportedModule::kFindOnly);
705 E : }
706 :
707 : // Query the imports to see which ones are present.
708 E : if (!find_imports.TransformBlockGraph(policy, block_graph, header_block)) {
709 i : LOG(ERROR) << "Unable to find imports for redirection.";
710 i : return false;
711 : }
712 :
713 : // Add Asan imports for those functions found in the import tables. These will
714 : // later be redirected.
715 E : for (const auto& module : *imported_modules) {
716 E : for (size_t i = 0; i < module->size(); ++i) {
717 E : if (!module->SymbolIsImported(i))
718 E : continue;
719 :
720 : // The function should not already be imported. If it is then the
721 : // intercepts data contains duplicates.
722 E : const std::string& function_name = module->GetSymbolName(i);
723 E : DCHECK(import_name_index_map->find(function_name) ==
724 : import_name_index_map->end());
725 :
726 E : std::string asan_function_name = kUndecoratedAsanInterceptPrefix;
727 E : asan_function_name += function_name;
728 : size_t index = asan_rtl->AddSymbol(asan_function_name,
729 E : ImportedModule::kAlwaysImport);
730 E : import_name_index_map->insert(std::make_pair(function_name, index));
731 E : }
732 E : }
733 :
734 E : return true;
735 E : }
736 :
737 : void PeFindStaticallyLinkedFunctionsToIntercept(
738 : bool use_interceptors,
739 : const AsanIntercept* intercepts,
740 : BlockGraph* block_graph,
741 : std::vector<BlockGraph::Block*>* static_blocks,
742 : ImportNameIndexMap* import_name_index_map,
743 E : ImportedModule* asan_rtl) {
744 E : DCHECK_NE(reinterpret_cast<AsanIntercept*>(NULL), intercepts);
745 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
746 : DCHECK_NE(reinterpret_cast<std::vector<BlockGraph::Block*>*>(NULL),
747 E : static_blocks);
748 E : DCHECK_NE(reinterpret_cast<ImportNameIndexMap*>(NULL), import_name_index_map);
749 E : DCHECK_NE(reinterpret_cast<ImportedModule*>(NULL), asan_rtl);
750 :
751 : // Populate the filter with known hashes.
752 E : AsanInterceptorFilter filter;
753 E : filter.InitializeContentHashes(intercepts, use_interceptors);
754 E : if (filter.empty())
755 E : return;
756 :
757 : // Discover statically linked functions that need to be intercepted.
758 : BlockGraph::BlockMap::iterator block_it =
759 E : block_graph->blocks_mutable().begin();
760 E : for (; block_it != block_graph->blocks_mutable().end(); ++block_it) {
761 E : BlockGraph::Block* block = &block_it->second;
762 E : if (!filter.ShouldIntercept(block))
763 E : continue;
764 E : static_blocks->push_back(block);
765 :
766 : // Don't add an import entry for names that have already been processed.
767 : if (import_name_index_map->find(block->name()) !=
768 E : import_name_index_map->end()) {
769 i : continue;
770 : }
771 :
772 E : std::string name = kUndecoratedAsanInterceptPrefix;
773 E : name += block->name();
774 : size_t index = asan_rtl->AddSymbol(name,
775 E : ImportedModule::kAlwaysImport);
776 E : import_name_index_map->insert(std::make_pair(block->name(), index));
777 E : }
778 E : }
779 :
780 : void PeGetRedirectsForInterceptedImports(
781 : const ScopedVector<ImportedModule>& imported_modules,
782 : const ImportNameIndexMap& import_name_index_map,
783 : const ImportedModule& asan_rtl,
784 E : pe::ReferenceMap* reference_redirect_map) {
785 E : DCHECK_NE(reinterpret_cast<pe::ReferenceMap*>(NULL), reference_redirect_map);
786 :
787 : // Register redirections related to the original.
788 E : for (const auto& module : imported_modules) {
789 E : for (size_t j = 0; j < module->size(); ++j) {
790 E : if (!module->SymbolIsImported(j))
791 E : continue;
792 :
793 : // Get a reference to the original import.
794 E : BlockGraph::Reference src;
795 E : CHECK(module->GetSymbolReference(j, &src));
796 :
797 : // Get a reference to the newly created import.
798 E : const std::string& name = module->GetSymbolName(j);
799 : ImportNameIndexMap::const_iterator import_it =
800 E : import_name_index_map.find(name);
801 E : DCHECK(import_it != import_name_index_map.end());
802 E : BlockGraph::Reference dst;
803 E : CHECK(asan_rtl.GetSymbolReference(import_it->second, &dst));
804 :
805 : // Record the reference mapping.
806 : reference_redirect_map->insert(
807 : std::make_pair(pe::ReferenceDest(src.referenced(), src.offset()),
808 E : pe::ReferenceDest(dst.referenced(), dst.offset())));
809 E : }
810 E : }
811 E : }
812 :
813 : bool PeGetRedirectsForStaticallyLinkedFunctions(
814 : const std::vector<BlockGraph::Block*>& static_blocks,
815 : const ImportNameIndexMap& import_name_index_map,
816 : const ImportedModule& asan_rtl,
817 : BlockGraph* block_graph,
818 E : pe::ReferenceMap* reference_redirect_map) {
819 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
820 E : DCHECK_NE(reinterpret_cast<pe::ReferenceMap*>(NULL), reference_redirect_map);
821 :
822 : BlockGraph::Section* thunk_section = block_graph->FindOrAddSection(
823 E : common::kThunkSectionName, pe::kCodeCharacteristics);
824 E : DCHECK_NE(reinterpret_cast<BlockGraph::Section*>(NULL), thunk_section);
825 :
826 : typedef std::map<std::string, BlockGraph::Block*> ThunkMap;
827 E : ThunkMap thunk_map;
828 E : for (size_t i = 0; i < static_blocks.size(); ++i) {
829 E : BlockGraph::Block* block = static_blocks[i];
830 :
831 E : ThunkMap::iterator thunk_it = thunk_map.find(block->name());
832 E : if (thunk_it == thunk_map.end()) {
833 : // Generate the name of the thunk for this function.
834 E : std::string thunk_name = kUndecoratedAsanInterceptPrefix;
835 E : thunk_name += block->name();
836 E : thunk_name += "_thunk";
837 :
838 : // Get a reference to the newly created import.
839 : ImportNameIndexMap::const_iterator import_it =
840 E : import_name_index_map.find(block->name());
841 E : DCHECK(import_it != import_name_index_map.end());
842 E : BlockGraph::Reference import_ref;
843 E : CHECK(asan_rtl.GetSymbolReference(import_it->second, &import_ref));
844 :
845 : // Generate a basic code block for this thunk.
846 E : BasicBlockSubGraph bbsg;
847 : BasicBlockSubGraph::BlockDescription* block_desc =
848 : bbsg.AddBlockDescription(thunk_name,
849 : thunk_section->name(),
850 : BlockGraph::CODE_BLOCK,
851 : thunk_section->id(),
852 : 1,
853 E : 0);
854 :
855 E : BasicCodeBlock* bb = bbsg.AddBasicCodeBlock(thunk_name);
856 E : block_desc->basic_block_order.push_back(bb);
857 : BasicBlockAssembler assm(bb->instructions().begin(),
858 E : &bb->instructions());
859 : assm.jmp(Operand(Displacement(import_ref.referenced(),
860 E : import_ref.offset())));
861 :
862 : // Condense into a block.
863 E : BlockBuilder block_builder(block_graph);
864 E : if (!block_builder.Merge(&bbsg)) {
865 i : LOG(ERROR) << "Failed to build thunk block \"" << thunk_name << "\".";
866 i : return false;
867 : }
868 :
869 : // Exactly one new block should have been created.
870 E : DCHECK_EQ(1u, block_builder.new_blocks().size());
871 E : BlockGraph::Block* thunk = block_builder.new_blocks().front();
872 E : thunk_it = thunk_map.insert(std::make_pair(block->name(), thunk)).first;
873 E : }
874 E : DCHECK(thunk_it != thunk_map.end());
875 :
876 : // Register a redirection of references, from the original block to the
877 : // newly created thunk.
878 : reference_redirect_map->insert(std::make_pair(
879 : pe::ReferenceDest(block, 0),
880 E : pe::ReferenceDest(thunk_it->second, 0)));
881 E : }
882 :
883 E : return true;
884 E : }
885 :
886 : } // namespace
887 :
888 : const char AsanBasicBlockTransform::kTransformName[] =
889 : "SyzyAsanBasicBlockTransform";
890 :
891 : bool AsanBasicBlockTransform::InstrumentBasicBlock(
892 : BasicCodeBlock* basic_block,
893 : StackAccessMode stack_mode,
894 E : BlockGraph::ImageFormat image_format) {
895 E : DCHECK_NE(reinterpret_cast<BasicCodeBlock*>(NULL), basic_block);
896 :
897 E : if (instrumentation_rate_ == 0.0)
898 E : return true;
899 :
900 : // Pre-compute liveness information for each instruction.
901 E : std::list<LivenessAnalysis::State> states;
902 E : LivenessAnalysis::State state;
903 E : if (use_liveness_analysis_) {
904 E : liveness_.GetStateAtExitOf(basic_block, &state);
905 :
906 : BasicBlock::Instructions::reverse_iterator rev_iter_inst =
907 E : basic_block->instructions().rbegin();
908 : BasicBlock::Instructions::const_reverse_iterator rev_iter_inst_end =
909 E : basic_block->instructions().rend();
910 E : for (; rev_iter_inst != rev_iter_inst_end; ++rev_iter_inst) {
911 E : const Instruction& instr = *rev_iter_inst;
912 E : liveness_.PropagateBackward(instr, &state);
913 E : states.push_front(state);
914 E : }
915 :
916 E : DCHECK_EQ(states.size(), basic_block->instructions().size());
917 : }
918 :
919 : // Get the memory accesses information for this basic block.
920 E : MemoryAccessAnalysis::State memory_state;
921 E : if (remove_redundant_checks_)
922 E : memory_accesses_.GetStateAtEntryOf(basic_block, &memory_state);
923 :
924 : // Process each instruction and inject a call to Asan when we find an
925 : // instrumentable memory access.
926 : BasicBlock::Instructions::iterator iter_inst =
927 E : basic_block->instructions().begin();
928 E : std::list<LivenessAnalysis::State>::iterator iter_state = states.begin();
929 E : for (; iter_inst != basic_block->instructions().end(); ++iter_inst) {
930 E : auto operand(Operand(assm::eax));
931 E : const Instruction& instr = *iter_inst;
932 E : const _DInst& repr = instr.representation();
933 :
934 : MemoryAccessInfo info;
935 E : info.mode = kNoAccess;
936 E : info.size = 0;
937 E : info.opcode = 0;
938 E : info.save_flags = true;
939 :
940 : // Get current instruction liveness information.
941 E : if (use_liveness_analysis_) {
942 E : state = *iter_state;
943 E : ++iter_state;
944 : }
945 :
946 : // When activated, skip redundant memory access check.
947 E : if (remove_redundant_checks_) {
948 E : bool need_memory_access_check = false;
949 E : if (memory_state.HasNonRedundantAccess(instr))
950 E : need_memory_access_check = true;
951 :
952 : // Update the memory accesses information for the current instruction.
953 E : memory_accesses_.PropagateForward(instr, &memory_state);
954 :
955 E : if (!need_memory_access_check)
956 E : continue;
957 : }
958 :
959 : // Insert hook for a standard instruction.
960 E : if (!DecodeMemoryAccess(instr, &operand, &info))
961 E : continue;
962 :
963 : // Bail if this is not a memory access.
964 E : if (info.mode == kNoAccess)
965 i : continue;
966 :
967 : // A basic block reference means that can be either a computed jump,
968 : // or a load from a case table. In either case it doesn't make sense
969 : // to instrument the access.
970 : if (operand.displacement().reference().referred_type() ==
971 E : BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK) {
972 E : continue;
973 : }
974 :
975 : // A block reference means this instruction is reading or writing to
976 : // a global variable or some such. It's viable to pad and align global
977 : // variables and to red-zone the padding, but without that, there's nothing
978 : // to gain by instrumenting these accesses.
979 : if (operand.displacement().reference().referred_type() ==
980 E : BasicBlockReference::REFERRED_TYPE_BLOCK) {
981 E : continue;
982 : }
983 :
984 : // Is this an instruction we should be instrumenting.
985 E : if (!ShouldInstrumentOpcode(repr.opcode))
986 E : continue;
987 :
988 : // If there are no unconventional manipulations of the stack frame, we can
989 : // skip instrumenting stack-based memory access (based on ESP or EBP).
990 : // Conventionally, accesses through ESP/EBP are always on stack.
991 : if (stack_mode == kSafeStackAccess &&
992 : (operand.base() == assm::kRegisterEsp ||
993 E : operand.base() == assm::kRegisterEbp)) {
994 E : continue;
995 : }
996 :
997 : // We do not instrument memory accesses through special segments.
998 : // FS is used for thread local specifics and GS for CPU info.
999 E : uint8_t segment = SEGMENT_GET(repr.segment);
1000 E : if (segment == R_FS || segment == R_GS)
1001 E : continue;
1002 :
1003 : // Don't instrument any filtered instructions.
1004 E : if (IsFiltered(*iter_inst))
1005 E : continue;
1006 :
1007 : // Randomly sample to effect partial instrumentation.
1008 : if (instrumentation_rate_ < 1.0 &&
1009 E : base::RandDouble() >= instrumentation_rate_) {
1010 E : continue;
1011 : }
1012 :
1013 : // Create a BasicBlockAssembler to insert new instruction.
1014 E : BasicBlockAssembler bb_asm(iter_inst, &basic_block->instructions());
1015 :
1016 : // Configure the assembler to copy the SourceRange information of the
1017 : // current instrumented instruction into newly created instructions. This is
1018 : // a hack to allow valid stack walking and better error reporting, but
1019 : // breaks the 1:1 OMAP mapping and may confuse some debuggers.
1020 E : if (debug_friendly_)
1021 E : bb_asm.set_source_range(instr.source_range());
1022 :
1023 : if (use_liveness_analysis_ &&
1024 E : (info.mode == kReadAccess || info.mode == kWriteAccess)) {
1025 : // Use the liveness information to skip saving the flags if possible.
1026 E : info.save_flags = state.AreArithmeticFlagsLive();
1027 : }
1028 :
1029 : // Mark that an instrumentation will happen. Do this before selecting a
1030 : // hook so we can call a dry run without hooks present.
1031 E : instrumentation_happened_ = true;
1032 :
1033 E : if (!dry_run_) {
1034 : // Insert hook for standard instructions.
1035 E : AsanHookMap::iterator hook = check_access_hooks_->find(info);
1036 E : if (hook == check_access_hooks_->end()) {
1037 i : LOG(ERROR) << "Invalid access : "
1038 : << GetAsanCheckAccessFunctionName(info, image_format);
1039 i : return false;
1040 : }
1041 :
1042 : // Instrument this instruction.
1043 : InjectAsanHook(
1044 E : &bb_asm, info, operand, &hook->second, state, image_format);
1045 : }
1046 :
1047 E : }
1048 :
1049 E : DCHECK(iter_state == states.end());
1050 :
1051 E : return true;
1052 E : }
1053 :
1054 : void AsanBasicBlockTransform::set_instrumentation_rate(
1055 E : double instrumentation_rate) {
1056 : // Set the instrumentation rate, capping it between 0 and 1.
1057 E : instrumentation_rate_ = std::max(0.0, std::min(1.0, instrumentation_rate));
1058 E : }
1059 :
1060 : bool AsanBasicBlockTransform::TransformBasicBlockSubGraph(
1061 : const TransformPolicyInterface* policy,
1062 : BlockGraph* block_graph,
1063 E : BasicBlockSubGraph* subgraph) {
1064 E : DCHECK(policy != NULL);
1065 E : DCHECK(block_graph != NULL);
1066 E : DCHECK(subgraph != NULL);
1067 :
1068 : // Perform a global liveness analysis.
1069 E : if (use_liveness_analysis_)
1070 E : liveness_.Analyze(subgraph);
1071 :
1072 : // Perform a redundant memory access analysis.
1073 E : if (remove_redundant_checks_)
1074 E : memory_accesses_.Analyze(subgraph);
1075 :
1076 : // Determines if this subgraph uses unconventional stack pointer
1077 : // manipulations.
1078 E : StackAccessMode stack_mode = kUnsafeStackAccess;
1079 E : if (!block_graph::HasUnexpectedStackFrameManipulation(subgraph))
1080 E : stack_mode = kSafeStackAccess;
1081 :
1082 : // Iterates through each basic block and instruments it.
1083 : BasicBlockSubGraph::BBCollection::iterator it =
1084 E : subgraph->basic_blocks().begin();
1085 E : for (; it != subgraph->basic_blocks().end(); ++it) {
1086 E : BasicCodeBlock* bb = BasicCodeBlock::Cast(*it);
1087 : if (bb != NULL &&
1088 E : !InstrumentBasicBlock(bb, stack_mode, block_graph->image_format())) {
1089 i : return false;
1090 : }
1091 E : }
1092 E : return true;
1093 E : }
1094 :
1095 : const char AsanTransform::kTransformName[] = "SyzyAsanTransform";
1096 :
1097 : const char AsanTransform::kAsanHookStubName[] = "asan_hook_stub";
1098 :
1099 : const char AsanTransform::kSyzyAsanDll[] = "syzyasan_rtl.dll";
1100 :
1101 : AsanTransform::AsanTransform()
1102 : : asan_dll_name_(kSyzyAsanDll),
1103 : debug_friendly_(false),
1104 : use_liveness_analysis_(false),
1105 : remove_redundant_checks_(false),
1106 : use_interceptors_(false),
1107 : instrumentation_rate_(1.0),
1108 : asan_parameters_(NULL),
1109 : check_access_hooks_ref_(),
1110 E : asan_parameters_block_(NULL) {
1111 E : }
1112 :
1113 E : void AsanTransform::set_instrumentation_rate(double instrumentation_rate) {
1114 : // Set the instrumentation rate, capping it between 0 and 1.
1115 E : instrumentation_rate_ = std::max(0.0, std::min(1.0, instrumentation_rate));
1116 E : }
1117 :
1118 : bool AsanTransform::PreBlockGraphIteration(
1119 : const TransformPolicyInterface* policy,
1120 : BlockGraph* block_graph,
1121 E : BlockGraph::Block* header_block) {
1122 E : DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
1123 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
1124 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), header_block);
1125 : DCHECK(block_graph->image_format() == BlockGraph::PE_IMAGE ||
1126 E : block_graph->image_format() == BlockGraph::COFF_IMAGE);
1127 :
1128 : // Ensure that this image has not already been instrumented.
1129 E : if (block_graph->FindSection(common::kThunkSectionName)) {
1130 i : LOG(ERROR) << "The image is already instrumented.";
1131 i : return false;
1132 : }
1133 :
1134 E : AccessHookParamVector access_hook_param_vec;
1135 E : AsanBasicBlockTransform::AsanDefaultHookMap default_stub_map;
1136 :
1137 : // We only need to add stubs for PE images. COFF images use direct references,
1138 : // and the linker takes care of dragging in the appropriate code for us.
1139 E : if (block_graph->image_format() == BlockGraph::PE_IMAGE) {
1140 : // Create the hook stub for read/write instructions.
1141 E : BlockGraph::Reference read_write_hook;
1142 : if (!CreateHooksStub(block_graph, kAsanHookStubName,
1143 : AsanBasicBlockTransform::kReadAccess,
1144 E : &read_write_hook)) {
1145 i : return false;
1146 : }
1147 :
1148 : // Create the hook stub for strings instructions.
1149 E : BlockGraph::Reference instr_hook;
1150 : if (!CreateHooksStub(block_graph, kAsanHookStubName,
1151 : AsanBasicBlockTransform::kInstrAccess,
1152 E : &instr_hook)) {
1153 i : return false;
1154 : }
1155 :
1156 : // Map each memory access kind to an appropriate stub.
1157 E : default_stub_map[AsanBasicBlockTransform::kReadAccess] = read_write_hook;
1158 E : default_stub_map[AsanBasicBlockTransform::kWriteAccess] = read_write_hook;
1159 E : default_stub_map[AsanBasicBlockTransform::kInstrAccess] = instr_hook;
1160 E : default_stub_map[AsanBasicBlockTransform::kRepzAccess] = instr_hook;
1161 E : default_stub_map[AsanBasicBlockTransform::kRepnzAccess] = instr_hook;
1162 : }
1163 :
1164 : // Add an import entry for the Asan runtime.
1165 E : ImportedModule import_module(asan_dll_name_, kDateInThePast);
1166 :
1167 : // Import the hooks for the read/write accesses.
1168 E : for (int access_size = 1; access_size <= 32; access_size *= 2) {
1169 : MemoryAccessInfo read_info =
1170 E : { AsanBasicBlockTransform::kReadAccess, access_size, 0, true };
1171 E : access_hook_param_vec.push_back(read_info);
1172 E : if (use_liveness_analysis()) {
1173 E : read_info.save_flags = false;
1174 E : access_hook_param_vec.push_back(read_info);
1175 : }
1176 :
1177 : MemoryAccessInfo write_info =
1178 E : { AsanBasicBlockTransform::kWriteAccess, access_size, 0, true };
1179 E : access_hook_param_vec.push_back(write_info);
1180 E : if (use_liveness_analysis()) {
1181 E : write_info.save_flags = false;
1182 E : access_hook_param_vec.push_back(write_info);
1183 : }
1184 E : }
1185 :
1186 : // Import the hooks for the read/write 10-bytes accesses.
1187 : MemoryAccessInfo read_info_10 =
1188 E : { AsanBasicBlockTransform::kReadAccess, 10, 0, true };
1189 E : access_hook_param_vec.push_back(read_info_10);
1190 E : if (use_liveness_analysis()) {
1191 E : read_info_10.save_flags = false;
1192 E : access_hook_param_vec.push_back(read_info_10);
1193 : }
1194 :
1195 : MemoryAccessInfo write_info_10 =
1196 E : { AsanBasicBlockTransform::kWriteAccess, 10, 0, true };
1197 E : access_hook_param_vec.push_back(write_info_10);
1198 E : if (use_liveness_analysis()) {
1199 E : write_info_10.save_flags = false;
1200 E : access_hook_param_vec.push_back(write_info_10);
1201 : }
1202 :
1203 : // Import the hooks for strings/prefix memory accesses.
1204 E : const _InstructionType strings[] = { I_CMPS, I_MOVS, I_STOS };
1205 E : int strings_length = sizeof(strings)/sizeof(_InstructionType);
1206 :
1207 E : for (int access_size = 1; access_size <= 4; access_size *= 2) {
1208 E : for (int inst = 0; inst < strings_length; ++inst) {
1209 : MemoryAccessInfo repz_inst_info = {
1210 : AsanBasicBlockTransform::kRepzAccess,
1211 : access_size,
1212 : strings[inst],
1213 : true
1214 E : };
1215 E : access_hook_param_vec.push_back(repz_inst_info);
1216 :
1217 : MemoryAccessInfo inst_info = {
1218 : AsanBasicBlockTransform::kInstrAccess,
1219 : access_size,
1220 : strings[inst],
1221 : true
1222 E : };
1223 E : access_hook_param_vec.push_back(inst_info);
1224 E : }
1225 E : }
1226 :
1227 : if (!AddAsanCheckAccessHooks(access_hook_param_vec,
1228 : default_stub_map,
1229 : &import_module,
1230 : &check_access_hooks_ref_,
1231 : policy,
1232 : block_graph,
1233 E : header_block)) {
1234 i : return false;
1235 : }
1236 E : return true;
1237 E : }
1238 :
1239 : bool AsanTransform::OnBlock(const TransformPolicyInterface* policy,
1240 : BlockGraph* block_graph,
1241 E : BlockGraph::Block* block) {
1242 E : DCHECK(policy != NULL);
1243 E : DCHECK(block_graph != NULL);
1244 E : DCHECK(block != NULL);
1245 :
1246 E : if (!policy->BlockIsSafeToBasicBlockDecompose(block))
1247 E : return true;
1248 :
1249 : // Use the filter that was passed to us for our child transform.
1250 E : AsanBasicBlockTransform transform(&check_access_hooks_ref_);
1251 E : transform.set_debug_friendly(debug_friendly());
1252 E : transform.set_use_liveness_analysis(use_liveness_analysis());
1253 E : transform.set_remove_redundant_checks(remove_redundant_checks());
1254 E : transform.set_filter(filter());
1255 E : transform.set_instrumentation_rate(instrumentation_rate_);
1256 :
1257 : if (!ApplyBasicBlockSubGraphTransform(
1258 E : &transform, policy, block_graph, block, NULL)) {
1259 i : return false;
1260 : }
1261 :
1262 E : return true;
1263 E : }
1264 :
1265 : bool AsanTransform::PostBlockGraphIteration(
1266 : const TransformPolicyInterface* policy,
1267 : BlockGraph* block_graph,
1268 E : BlockGraph::Block* header_block) {
1269 E : DCHECK(policy != NULL);
1270 E : DCHECK(block_graph != NULL);
1271 E : DCHECK(header_block != NULL);
1272 :
1273 E : if (block_graph->image_format() == BlockGraph::PE_IMAGE) {
1274 : if (!PeInterceptFunctions(kAsanIntercepts, policy, block_graph,
1275 E : header_block)) {
1276 i : return false;
1277 : }
1278 :
1279 E : if (!PeInjectAsanParameters(policy, block_graph, header_block))
1280 i : return false;
1281 E : } else {
1282 E : DCHECK_EQ(BlockGraph::COFF_IMAGE, block_graph->image_format());
1283 : if (!CoffInterceptFunctions(kAsanIntercepts, policy, block_graph,
1284 E : header_block)) {
1285 i : return false;
1286 : }
1287 : }
1288 :
1289 E : if (!PatchCRTHeapInitialization(block_graph, header_block, policy))
1290 i : return false;
1291 :
1292 E : return true;
1293 E : }
1294 :
1295 : bool AsanTransform::PeInterceptFunctions(
1296 : const AsanIntercept* intercepts,
1297 : const TransformPolicyInterface* policy,
1298 : BlockGraph* block_graph,
1299 E : BlockGraph::Block* header_block) {
1300 E : DCHECK_NE(reinterpret_cast<AsanIntercept*>(NULL), intercepts);
1301 E : DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
1302 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
1303 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), header_block);
1304 E : DCHECK_EQ(BlockGraph::PE_IMAGE, block_graph->image_format());
1305 :
1306 : // This is used to keep track of the index of imports to the Asan RTL.
1307 E : ImportNameIndexMap import_name_index_map;
1308 :
1309 : // Keeps track of all imported modules with imports that we intercept.
1310 E : ScopedVector<ImportedModule> imported_modules;
1311 :
1312 E : ImportedModule asan_rtl(asan_dll_name_, kDateInThePast);
1313 :
1314 : // Determines what PE imports need to be intercepted, adding them to
1315 : // |asan_rtl| and |import_name_index_map|.
1316 : if (!PeFindImportsToIntercept(use_interceptors_,
1317 : intercepts,
1318 : policy,
1319 : block_graph,
1320 : header_block,
1321 : &imported_modules,
1322 : &import_name_index_map,
1323 E : &asan_rtl)) {
1324 i : return false;
1325 : }
1326 :
1327 : // Keep track of how many import redirections are to be performed. This allows
1328 : // a minor optimization later on when there are none to be performed.
1329 E : size_t import_redirection_count = asan_rtl.size();
1330 :
1331 : // Find statically linked function blocks to intercept, adding them to
1332 : // |asan_rtl| and |import_name_index_map|.
1333 E : std::vector<BlockGraph::Block*> static_blocks;
1334 : PeFindStaticallyLinkedFunctionsToIntercept(use_interceptors_,
1335 : intercepts,
1336 : block_graph,
1337 : &static_blocks,
1338 : &import_name_index_map,
1339 E : &asan_rtl);
1340 :
1341 : // If no imports were found at all, then there are no redirections to perform.
1342 E : if (asan_rtl.size() == 0)
1343 i : return true;
1344 :
1345 : // Add the Asan RTL imports to the image.
1346 E : PEAddImportsTransform add_imports_transform;
1347 E : add_imports_transform.AddModule(&asan_rtl);
1348 : if (!add_imports_transform.TransformBlockGraph(
1349 E : policy, block_graph, header_block)) {
1350 i : LOG(ERROR) << "Unable to add imports for redirection.";
1351 i : return false;
1352 : }
1353 :
1354 : // This keeps track of reference redirections that need to be performed.
1355 E : pe::ReferenceMap reference_redirect_map;
1356 :
1357 E : if (import_redirection_count > 0) {
1358 : PeGetRedirectsForInterceptedImports(imported_modules,
1359 : import_name_index_map,
1360 : asan_rtl,
1361 E : &reference_redirect_map);
1362 : }
1363 :
1364 : // Adds redirect information for any intercepted statically linked functions.
1365 E : if (!static_blocks.empty()) {
1366 : if (!PeGetRedirectsForStaticallyLinkedFunctions(static_blocks,
1367 : import_name_index_map,
1368 : asan_rtl,
1369 : block_graph,
1370 E : &reference_redirect_map)) {
1371 i : return false;
1372 : }
1373 : }
1374 :
1375 : // Finally, redirect all references to intercepted functions.
1376 E : pe::RedirectReferences(reference_redirect_map);
1377 :
1378 E : return true;
1379 E : }
1380 :
1381 : bool AsanTransform::PeInjectAsanParameters(
1382 : const TransformPolicyInterface* policy,
1383 : BlockGraph* block_graph,
1384 E : BlockGraph::Block* header_block) {
1385 E : DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
1386 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
1387 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), header_block);
1388 E : DCHECK_EQ(BlockGraph::PE_IMAGE, block_graph->image_format());
1389 :
1390 : // If there are no parameters then do nothing.
1391 E : if (asan_parameters_ == NULL)
1392 E : return true;
1393 :
1394 : // Serialize the parameters into a new block.
1395 E : common::FlatAsanParameters fparams(*asan_parameters_);
1396 : BlockGraph::Block* params_block = block_graph->AddBlock(
1397 E : BlockGraph::DATA_BLOCK, fparams.data().size(), "AsanParameters");
1398 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), params_block);
1399 E : params_block->CopyData(fparams.data().size(), fparams.data().data());
1400 :
1401 : // Wire up any references that are required.
1402 : COMPILE_ASSERT(8 == common::kAsanParametersVersion,
1403 : pointers_in_the_params_must_be_linked_up_here);
1404 E : block_graph::TypedBlock<common::AsanParameters> params;
1405 E : CHECK(params.Init(0, params_block));
1406 E : if (fparams->ignored_stack_ids != NULL) {
1407 : size_t offset = reinterpret_cast<const uint8*>(fparams->ignored_stack_ids) -
1408 E : reinterpret_cast<const uint8*>(&fparams.params());
1409 : CHECK(params.SetReference(BlockGraph::ABSOLUTE_REF,
1410 : params->ignored_stack_ids,
1411 : params_block,
1412 : offset,
1413 E : offset));
1414 : }
1415 :
1416 : // Create an appropriately named section and put the parameters there. The
1417 : // RTL looks for this named section to find the parameters.
1418 : BlockGraph::Section* section = block_graph->FindOrAddSection(
1419 : common::kAsanParametersSectionName,
1420 E : common::kAsanParametersSectionCharacteristics);
1421 E : DCHECK_NE(reinterpret_cast<BlockGraph::Section*>(NULL), section);
1422 E : params_block->set_section(section->id());
1423 :
1424 : // Remember the block containing the parameters. This is a unittesting seam.
1425 E : asan_parameters_block_ = params_block;
1426 :
1427 E : return true;
1428 E : }
1429 :
1430 : bool AsanTransform::CoffInterceptFunctions(
1431 : const AsanIntercept* intercepts,
1432 : const TransformPolicyInterface* policy,
1433 : BlockGraph* block_graph,
1434 E : BlockGraph::Block* header_block) {
1435 E : DCHECK_NE(reinterpret_cast<AsanIntercept*>(NULL), intercepts);
1436 E : DCHECK_NE(reinterpret_cast<TransformPolicyInterface*>(NULL), policy);
1437 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
1438 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), header_block);
1439 :
1440 : // Extract the existing symbols.
1441 E : pe::CoffSymbolNameOffsetMap symbol_map;
1442 E : BlockGraph::Block* symbols_block = NULL;
1443 E : BlockGraph::Block* strings_block = NULL;
1444 : if (!pe::FindCoffSpecialBlocks(block_graph, NULL, &symbols_block,
1445 E : &strings_block)) {
1446 i : LOG(ERROR) << "Unable to find COFF header blocks.";
1447 i : return false;
1448 : }
1449 E : if (!pe::BuildCoffSymbolNameOffsetMap(block_graph, &symbol_map)) {
1450 i : LOG(ERROR) << "Unable to build symbol map.";
1451 i : return false;
1452 : }
1453 :
1454 : // Populate a COFF symbol rename transform for each function to be
1455 : // intercepted. We simply try to rename all possible symbols that may exist
1456 : // and allow the transform to ignore any that aren't present.
1457 E : pe::transforms::CoffRenameSymbolsTransform rename_tx;
1458 E : rename_tx.set_symbols_must_exist(false);
1459 E : const AsanIntercept* intercept = intercepts;
1460 E : bool defines_asan_functions = false;
1461 E : for (; intercept->undecorated_name != NULL; ++intercept) {
1462 : // Skip disabled optional functions.
1463 E : if (!use_interceptors_ && intercept->optional)
1464 i : continue;
1465 :
1466 : // Skip functions for which we have no decorated name.
1467 E : if (intercept->decorated_name == NULL)
1468 E : continue;
1469 :
1470 : // Build the name of the imported version of this symbol.
1471 E : std::string imp_name(kDecoratedImportPrefix);
1472 E : imp_name += intercept->decorated_name;
1473 :
1474 : // Build the name of the Asan instrumented version of this symbol.
1475 E : std::string asan_name(kDecoratedAsanInterceptPrefix);
1476 E : asan_name += intercept->decorated_name;
1477 :
1478 : // Build the name of the Asan instrumented imported version of this symbol.
1479 E : std::string imp_asan_name(kDecoratedImportPrefix);
1480 E : imp_asan_name += asan_name;
1481 :
1482 : // Build symbol rename mappings for the direct and indirect versions of the
1483 : // function.
1484 E : rename_tx.AddSymbolMapping(intercept->decorated_name, asan_name);
1485 E : rename_tx.AddSymbolMapping(imp_name, imp_asan_name);
1486 :
1487 : // We use the add imports transform to try to find names for the Asan
1488 : // implementation. If these already exist in the object file then our
1489 : // instrumentation will fail.
1490 E : const std::string* names[] = { &asan_name, &imp_asan_name };
1491 E : for (size_t i = 0; i < arraysize(names); ++i) {
1492 E : if (symbol_map.count(*names[i])) {
1493 i : LOG(ERROR) << "Object file being instrumented defines Asan function \""
1494 : << asan_name << "\".";
1495 i : defines_asan_functions = true;
1496 : }
1497 E : }
1498 E : }
1499 :
1500 E : if (defines_asan_functions)
1501 i : return false;
1502 :
1503 : // Apply the rename transform.
1504 : if (!block_graph::ApplyBlockGraphTransform(&rename_tx,
1505 : policy,
1506 : block_graph,
1507 E : header_block)) {
1508 i : LOG(ERROR) << "Failed to apply COFF symbol rename transform.";
1509 i : return false;
1510 : }
1511 :
1512 E : return true;
1513 E : }
1514 :
1515 : bool operator<(const AsanBasicBlockTransform::MemoryAccessInfo& left,
1516 E : const AsanBasicBlockTransform::MemoryAccessInfo& right) {
1517 E : if (left.mode != right.mode)
1518 E : return left.mode < right.mode;
1519 E : if (left.size != right.size)
1520 E : return left.size < right.size;
1521 E : if (left.save_flags != right.save_flags)
1522 E : return left.save_flags < right.save_flags;
1523 E : return left.opcode < right.opcode;
1524 E : }
1525 :
1526 : } // namespace transforms
1527 : } // namespace instrument
|