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