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