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 : // Unittests for the Asan transform.
16 :
17 : #include "syzygy/instrument/transforms/asan_transform.h"
18 :
19 : #include <set>
20 : #include <unordered_set>
21 : #include <vector>
22 :
23 : #include "base/scoped_native_library.h"
24 : #include "base/files/scoped_temp_dir.h"
25 : #include "base/strings/string_util.h"
26 : #include "base/strings/stringprintf.h"
27 : #include "base/win/pe_image.h"
28 : #include "gmock/gmock.h"
29 : #include "gtest/gtest.h"
30 : #include "syzygy/block_graph/basic_block_assembler.h"
31 : #include "syzygy/block_graph/block_hash.h"
32 : #include "syzygy/block_graph/unittest_util.h"
33 : #include "syzygy/common/defs.h"
34 : #include "syzygy/core/unittest_util.h"
35 : #include "syzygy/instrument/transforms/asan_intercepts.h"
36 : #include "syzygy/instrument/transforms/unittest_util.h"
37 : #include "syzygy/pe/coff_relinker.h"
38 : #include "syzygy/pe/coff_utils.h"
39 : #include "syzygy/pe/decomposer.h"
40 : #include "syzygy/pe/pe_file.h"
41 : #include "syzygy/pe/pe_relinker.h"
42 : #include "syzygy/pe/pe_utils.h"
43 : #include "syzygy/pe/unittest_util.h"
44 : #include "syzygy/pe/transforms/pe_add_imports_transform.h"
45 : #include "third_party/distorm/files/include/mnemonics.h"
46 :
47 : namespace instrument {
48 : namespace transforms {
49 :
50 : namespace {
51 :
52 : using block_graph::BasicBlock;
53 : using block_graph::BasicCodeBlock;
54 : using block_graph::BasicBlockSubGraph;
55 : using block_graph::BlockGraph;
56 : using block_graph::Instruction;
57 : using block_graph::RelativeAddressFilter;
58 : using core::RelativeAddress;
59 : using testing::ContainerEq;
60 : typedef AsanBasicBlockTransform::MemoryAccessMode AsanMemoryAccessMode;
61 : typedef AsanBasicBlockTransform::AsanHookMap HookMap;
62 : typedef AsanBasicBlockTransform::AsanHookMapEntryKey HookMapEntryKey;
63 :
64 : // Derived classes to expose protected members for unit-testing.
65 :
66 : class TestAsanBasicBlockTransform : public AsanBasicBlockTransform {
67 : public:
68 : using AsanBasicBlockTransform::InstrumentBasicBlock;
69 :
70 E : explicit TestAsanBasicBlockTransform(AsanHookMap* hooks_check_access)
71 E : : AsanBasicBlockTransform(hooks_check_access) {
72 E : }
73 : };
74 :
75 : class TestAsanInterceptorFilter : public AsanInterceptorFilter {
76 : public:
77 : using AsanInterceptorFilter::AddBlockToHashMap;
78 : };
79 :
80 : class TestAsanTransform : public AsanTransform {
81 : public:
82 : using AsanTransform::asan_parameters_block_;
83 : using AsanTransform::heap_init_blocks_;
84 : using AsanTransform::hot_patched_blocks_;
85 : using AsanTransform::static_intercepted_blocks_;
86 : using AsanTransform::use_interceptors_;
87 : using AsanTransform::use_liveness_analysis_;
88 : using AsanTransform::CoffInterceptFunctions;
89 : using AsanTransform::FindHeapInitAndCrtHeapBlocks;
90 : using AsanTransform::ShouldSkipBlock;
91 : using AsanTransform::PeFindStaticallyLinkedFunctionsToIntercept;
92 : using AsanTransform::PeInterceptFunctions;
93 : using AsanTransform::PeInjectAsanParameters;
94 : };
95 :
96 : class AsanTransformTest : public testing::TestDllTransformTest {
97 : public:
98 E : AsanTransformTest() : basic_block_(NULL) {
99 E : basic_block_ = subgraph_.AddBasicCodeBlock("dummy");
100 E : bb_asm_.reset(new block_graph::BasicBlockAssembler(
101 : basic_block_->instructions().begin(),
102 : &basic_block_->instructions()));
103 :
104 : // Insert a block description into the subgraph containing the dummy
105 : // basic block.
106 : BasicBlockSubGraph::BlockDescription* description =
107 E : subgraph_.AddBlockDescription("dummy_block", "dummy_compiland",
108 : BlockGraph::CODE_BLOCK, 0, 1, 0);
109 E : description->basic_block_order.push_back(basic_block_);
110 E : }
111 :
112 E : void ApplyTransformToIntegrationTestDll() {
113 E : base::FilePath input_path = ::testing::GetOutputRelativePath(
114 : testing::kIntegrationTestsDllName);
115 :
116 E : base::FilePath temp_dir;
117 E : CreateTemporaryDir(&temp_dir);
118 E : relinked_path_ = temp_dir.Append(testing::kIntegrationTestsDllName);
119 :
120 E : pe::PERelinker relinker(&pe_policy_);
121 E : relinker.set_input_path(input_path);
122 E : relinker.set_output_path(relinked_path_);
123 :
124 E : asan_transform_.use_interceptors_ = true;
125 E : asan_transform_.use_liveness_analysis_ = true;
126 E : relinker.AppendTransform(&asan_transform_);
127 E : ASSERT_TRUE(relinker.Init());
128 E : ASSERT_TRUE(relinker.Relink());
129 E : }
130 :
131 : void AddHookRef(const std::string& hook_name,
132 : AsanBasicBlockTransform::MemoryAccessMode access_kind,
133 : int access_size,
134 : uint16_t opcode,
135 E : bool save_flags) {
136 : HookMapEntryKey map_key = {
137 E : access_kind,
138 E : access_size,
139 E : opcode,
140 E : save_flags
141 : };
142 E : hooks_check_access_[map_key] =
143 : block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 4, hook_name);
144 : // Set up the references to the hooks needed by SyzyAsan.
145 E : hooks_check_access_ref_[map_key] =
146 : BlockGraph::Reference(BlockGraph::ABSOLUTE_REF, 4,
147 : hooks_check_access_[map_key], 0, 0);
148 E : }
149 :
150 E : void InitHooksRefs() {
151 : // Initialize the read access hooks.
152 E : for (int access_size = 1; access_size <= 8; access_size *= 2) {
153 : std::string name =
154 E : base::StringPrintf("asan_check_%d_byte_read_access", access_size);
155 E : AddHookRef(name, AsanBasicBlockTransform::kReadAccess, access_size, 0,
156 : true);
157 E : name += "_no_flags";
158 E : AddHookRef(name, AsanBasicBlockTransform::kReadAccess, access_size, 0,
159 : false);
160 E : }
161 : // Initialize the write access hooks.
162 E : for (int access_size = 1; access_size <= 8; access_size *= 2) {
163 : std::string name =
164 E : base::StringPrintf("asan_check_%d_byte_write_access", access_size);
165 E : AddHookRef(name, AsanBasicBlockTransform::kWriteAccess, access_size, 0,
166 : true);
167 E : name += "_no_flags";
168 E : AddHookRef(name, AsanBasicBlockTransform::kWriteAccess, access_size, 0,
169 : false);
170 E : }
171 :
172 E : const _InstructionType strings[] = { I_CMPS, I_MOVS, I_STOS };
173 E : int strings_length = arraysize(strings);
174 :
175 E : for (int access_size = 1; access_size <= 4; access_size *= 2) {
176 E : for (int inst = 0; inst < strings_length; ++inst) {
177 E : uint16_t opcode = strings[inst];
178 : const char* opcode_str =
179 E : reinterpret_cast<const char*>(GET_MNEMONIC_NAME(opcode));
180 : std::string name =
181 E : base::StringPrintf("asan_check_repz_%d_byte_%s_access",
182 : access_size, opcode_str);
183 E : name = base::ToLowerASCII(name);
184 E : AddHookRef(name, AsanBasicBlockTransform::kRepzAccess, access_size,
185 : opcode, true);
186 E : }
187 E : }
188 :
189 : // Initialize special instruction hooks.
190 E : for (int access_size = 1; access_size <= 4; access_size *= 2) {
191 E : for (int inst = 0; inst < strings_length; ++inst) {
192 E : uint16_t opcode = strings[inst];
193 : const char* opcode_str =
194 E : reinterpret_cast<const char*>(GET_MNEMONIC_NAME(opcode));
195 :
196 : // Initialize the strings without prefix access hooks.
197 : std::string name =
198 E : base::StringPrintf("asan_check_%d_byte_%s_access",
199 : access_size, opcode_str);
200 E : name = base::ToLowerASCII(name);
201 E : AddHookRef(name, AsanBasicBlockTransform::kInstrAccess, access_size,
202 : opcode, true);
203 :
204 : // Initialize the strings with prefix access hooks.
205 E : std::string repz_name = base::StringPrintf(
206 : "asan_check_repz_%d_byte_%s_access", access_size, opcode_str);
207 E : name = base::ToLowerASCII(repz_name);
208 E : AddHookRef(repz_name, AsanBasicBlockTransform::kRepzAccess, access_size,
209 : opcode, true);
210 E : }
211 E : }
212 E : }
213 :
214 E : bool AddInstructionFromBuffer(const uint8_t* data, size_t length) {
215 E : EXPECT_NE(static_cast<const uint8_t*>(NULL), data);
216 E : EXPECT_GE(assm::kMaxInstructionLength, length);
217 :
218 E : block_graph::Instruction temp;
219 E : if (!block_graph::Instruction::FromBuffer(data, length, &temp))
220 i : return false;
221 :
222 : // Append this instruction to the basic block.
223 E : basic_block_->instructions().push_back(temp);
224 :
225 E : return true;
226 E : }
227 :
228 : // Some handy constants we'll use throughout the tests.
229 : // @{
230 : static const BasicBlock::Size kDataSize;
231 : static const uint8_t kBlockData[];
232 : // @}
233 :
234 : protected:
235 : TestAsanTransform asan_transform_;
236 : HookMap hooks_check_access_ref_;
237 : std::map<HookMapEntryKey, BlockGraph::Block*> hooks_check_access_;
238 : BasicBlockSubGraph subgraph_;
239 : BasicCodeBlock* basic_block_;
240 : std::unique_ptr<block_graph::BasicBlockAssembler> bb_asm_;
241 : base::FilePath relinked_path_;
242 : };
243 :
244 : const BasicBlock::Size AsanTransformTest::kDataSize = 32;
245 : const uint8_t AsanTransformTest::kBlockData[AsanTransformTest::kDataSize] = {};
246 :
247 : // Dummy library name to test |set_instrument_dll_name|.
248 : const char kFooDll[] = "foo.dll";
249 :
250 : } // namespace
251 :
252 E : TEST_F(AsanTransformTest, SetAsanParameters) {
253 E : common::InflatedAsanParameters iparams;
254 E : common::InflatedAsanParameters* null = NULL;
255 E : common::InflatedAsanParameters* params = &iparams;
256 :
257 E : EXPECT_EQ(null, asan_transform_.asan_parameters());
258 E : asan_transform_.set_asan_parameters(params);
259 E : EXPECT_EQ(params, asan_transform_.asan_parameters());
260 E : asan_transform_.set_asan_parameters(NULL);
261 E : EXPECT_EQ(null, asan_transform_.asan_parameters());
262 E : }
263 :
264 E : TEST_F(AsanTransformTest, SetDryRunFlag) {
265 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
266 E : EXPECT_FALSE(bb_transform.dry_run());
267 E : bb_transform.set_dry_run(true);
268 E : EXPECT_TRUE(bb_transform.dry_run());
269 E : bb_transform.set_dry_run(false);
270 E : EXPECT_FALSE(bb_transform.dry_run());
271 E : }
272 :
273 E : TEST_F(AsanTransformTest, SetHotPatchingFlag) {
274 E : EXPECT_FALSE(asan_transform_.hot_patching());
275 E : asan_transform_.set_hot_patching(true);
276 E : EXPECT_TRUE(asan_transform_.hot_patching());
277 E : asan_transform_.set_hot_patching(false);
278 E : EXPECT_FALSE(asan_transform_.hot_patching());
279 E : }
280 :
281 E : TEST_F(AsanTransformTest, SetInstrumentDllName) {
282 E : EXPECT_EQ(AsanTransform::kSyzyAsanDll, asan_transform_.instrument_dll_name());
283 E : asan_transform_.set_instrument_dll_name(kFooDll);
284 E : EXPECT_EQ(kFooDll, asan_transform_.instrument_dll_name());
285 E : }
286 :
287 E : TEST_F(AsanTransformTest, SetInstrumentDllNameHotPatchingMode) {
288 : // The default dll name is different in hot patching mode.
289 E : asan_transform_.set_hot_patching(true);
290 E : EXPECT_EQ(AsanTransform::kSyzyAsanHpDll,
291 E : asan_transform_.instrument_dll_name());
292 E : asan_transform_.set_instrument_dll_name(kFooDll);
293 E : EXPECT_EQ(kFooDll, asan_transform_.instrument_dll_name());
294 E : }
295 :
296 E : TEST_F(AsanTransformTest, GetInstrumentationHappenedFlag) {
297 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
298 E : EXPECT_FALSE(bb_transform.instrumentation_happened());
299 E : }
300 :
301 E : TEST_F(AsanTransformTest, SetInstrumentationRate) {
302 E : EXPECT_EQ(1.0, asan_transform_.instrumentation_rate());
303 E : asan_transform_.set_instrumentation_rate(1.2);
304 E : EXPECT_EQ(1.0, asan_transform_.instrumentation_rate());
305 E : asan_transform_.set_instrumentation_rate(-0.2);
306 E : EXPECT_EQ(0.0, asan_transform_.instrumentation_rate());
307 E : asan_transform_.set_instrumentation_rate(0.5);
308 E : EXPECT_EQ(0.5, asan_transform_.instrumentation_rate());;
309 :
310 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
311 E : EXPECT_EQ(1.0, bb_transform.instrumentation_rate());
312 E : bb_transform.set_instrumentation_rate(1.2);
313 E : EXPECT_EQ(1.0, bb_transform.instrumentation_rate());
314 E : bb_transform.set_instrumentation_rate(-0.2);
315 E : EXPECT_EQ(0.0, bb_transform.instrumentation_rate());
316 E : bb_transform.set_instrumentation_rate(0.5);
317 E : EXPECT_EQ(0.5, bb_transform.instrumentation_rate());
318 E : }
319 :
320 E : TEST_F(AsanTransformTest, SetInterceptCRTFuntionsFlag) {
321 E : EXPECT_FALSE(asan_transform_.use_interceptors());
322 E : asan_transform_.set_use_interceptors(true);
323 E : EXPECT_TRUE(asan_transform_.use_interceptors());
324 E : asan_transform_.set_use_interceptors(false);
325 E : EXPECT_FALSE(asan_transform_.use_interceptors());
326 E : }
327 :
328 E : TEST_F(AsanTransformTest, SetRemoveRedundantChecksFlag) {
329 E : EXPECT_FALSE(asan_transform_.remove_redundant_checks());
330 E : asan_transform_.set_remove_redundant_checks(true);
331 E : EXPECT_TRUE(asan_transform_.remove_redundant_checks());
332 E : asan_transform_.set_remove_redundant_checks(false);
333 E : EXPECT_FALSE(asan_transform_.remove_redundant_checks());
334 :
335 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
336 E : EXPECT_FALSE(bb_transform.remove_redundant_checks());
337 E : bb_transform.set_remove_redundant_checks(true);
338 E : EXPECT_TRUE(bb_transform.remove_redundant_checks());
339 E : bb_transform.set_remove_redundant_checks(false);
340 E : EXPECT_FALSE(bb_transform.remove_redundant_checks());
341 E : }
342 :
343 E : TEST_F(AsanTransformTest, SetUseLivenessFlag) {
344 E : EXPECT_FALSE(asan_transform_.use_liveness_analysis());
345 E : asan_transform_.set_use_liveness_analysis(true);
346 E : EXPECT_TRUE(asan_transform_.use_liveness_analysis());
347 E : asan_transform_.set_use_liveness_analysis(false);
348 E : EXPECT_FALSE(asan_transform_.use_liveness_analysis());
349 :
350 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
351 E : EXPECT_FALSE(bb_transform.use_liveness_analysis());
352 E : bb_transform.set_use_liveness_analysis(true);
353 E : EXPECT_TRUE(bb_transform.use_liveness_analysis());
354 E : bb_transform.set_use_liveness_analysis(false);
355 E : EXPECT_FALSE(bb_transform.use_liveness_analysis());
356 E : }
357 :
358 E : TEST_F(AsanTransformTest, ApplyAsanTransformPE) {
359 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
360 :
361 E : asan_transform_.use_interceptors_ = true;
362 E : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
363 E : &asan_transform_, policy_, &block_graph_, header_block_));
364 E : }
365 :
366 E : TEST_F(AsanTransformTest, ApplyAsanTransformCoff) {
367 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDllObj());
368 :
369 E : asan_transform_.use_interceptors_ = true;
370 E : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
371 E : &asan_transform_, policy_, &block_graph_, header_block_));
372 E : }
373 :
374 E : TEST_F(AsanTransformTest, NopsNotInstrumented) {
375 : // Add all of the nops to the block.
376 : static const size_t kMaxNopSize =
377 : block_graph::BasicBlockAssembler::kMaxNopInstructionSize;
378 E : for (size_t i = 1; i <= kMaxNopSize; ++i)
379 E : bb_asm_->nop(i);
380 :
381 : // Add source ranges to the instruction.
382 E : block_graph::Instruction& i1 = *basic_block_->instructions().begin();
383 : Instruction::SourceRange source_range =
384 E : Instruction::SourceRange(RelativeAddress(1000), i1.size());
385 E : i1.set_source_range(source_range);
386 :
387 : // Instrument this basic block.
388 E : InitHooksRefs();
389 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
390 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
391 : basic_block_,
392 : AsanBasicBlockTransform::kSafeStackAccess,
393 E : BlockGraph::PE_IMAGE));
394 :
395 : // Expect that no instrumentation happened.
396 E : EXPECT_FALSE(bb_transform.instrumentation_happened());
397 :
398 : // Expect the same number of instructions as before the instrumentation.
399 E : ASSERT_EQ(basic_block_->instructions().size(), kMaxNopSize);
400 E : }
401 :
402 E : TEST_F(AsanTransformTest, InjectAsanHooksPe) {
403 : // Add a read access to the memory.
404 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ebx));
405 : // Add a write access to the memory.
406 E : bb_asm_->mov(block_graph::Operand(assm::ecx), assm::edx);
407 :
408 : // Add source ranges to the instruction.
409 E : block_graph::Instruction& i1 = *basic_block_->instructions().begin();
410 : Instruction::SourceRange source_range =
411 E : Instruction::SourceRange(RelativeAddress(1000), i1.size());
412 E : i1.set_source_range(source_range);
413 :
414 : // Instrument this basic block.
415 E : InitHooksRefs();
416 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
417 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
418 : basic_block_,
419 : AsanBasicBlockTransform::kSafeStackAccess,
420 E : BlockGraph::PE_IMAGE));
421 :
422 : // Ensure that the basic block is instrumented.
423 :
424 : // Check what the transform reports at first.
425 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
426 :
427 : // We had 2 instructions initially, and for each of them we add 3
428 : // instructions, so we expect to have 2 + 3 * 2 = 8 instructions.
429 E : ASSERT_EQ(basic_block_->instructions().size(), 8);
430 :
431 : // Walk through the instructions to ensure that the Asan hooks have been
432 : // injected.
433 : BasicBlock::Instructions::const_iterator iter_inst =
434 E : basic_block_->instructions().begin();
435 :
436 E : Instruction::SourceRange empty_source_range;
437 E : ASSERT_NE(empty_source_range, source_range);
438 :
439 : // First we check if the first memory access is instrumented as a 4 byte read
440 : // access. We also validate that the instrumentation has not had source range
441 : // information added.
442 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
443 E : ASSERT_EQ(I_PUSH, (iter_inst++)->representation().opcode);
444 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
445 E : ASSERT_EQ(I_LEA, (iter_inst++)->representation().opcode);
446 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
447 E : ASSERT_EQ(iter_inst->references().size(), 1);
448 : HookMapEntryKey check_4_byte_read_key =
449 E : { AsanBasicBlockTransform::kReadAccess, 4, 0, true };
450 E : ASSERT_EQ(hooks_check_access_[check_4_byte_read_key],
451 E : iter_inst->references().begin()->second.block());
452 E : ASSERT_EQ(O_DISP, iter_inst->representation().ops[0].type);
453 E : ASSERT_EQ(I_CALL, (iter_inst++)->representation().opcode);
454 E : ASSERT_EQ(I_MOV, (iter_inst++)->representation().opcode);
455 :
456 : // Then we check if the second memory access is well instrumented as a 4 byte
457 : // write access.
458 E : ASSERT_EQ(I_PUSH, (iter_inst++)->representation().opcode);
459 E : ASSERT_EQ(I_LEA, (iter_inst++)->representation().opcode);
460 E : ASSERT_EQ(iter_inst->references().size(), 1);
461 : HookMapEntryKey check_4_byte_write_key =
462 E : { AsanBasicBlockTransform::kWriteAccess, 4, 0, true };
463 E : ASSERT_EQ(hooks_check_access_[check_4_byte_write_key],
464 E : iter_inst->references().begin()->second.block());
465 E : ASSERT_EQ(I_CALL, (iter_inst++)->representation().opcode);
466 E : ASSERT_EQ(I_MOV, (iter_inst++)->representation().opcode);
467 :
468 E : ASSERT_TRUE(iter_inst == basic_block_->instructions().end());
469 E : }
470 :
471 E : TEST_F(AsanTransformTest, InjectAsanHooksWithSourceRangePe) {
472 : // Add a read access to the memory.
473 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ebx));
474 :
475 : // Add a source range to the instruction.
476 E : block_graph::Instruction& i1 = *basic_block_->instructions().begin();
477 : Instruction::SourceRange source_range =
478 E : Instruction::SourceRange(RelativeAddress(1000), i1.size());
479 E : i1.set_source_range(source_range);
480 :
481 : // Keep track of basic block size.
482 E : uint32_t before_instructions_count = basic_block_->instructions().size();
483 :
484 : // Instrument this basic block.
485 E : InitHooksRefs();
486 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
487 E : bb_transform.set_debug_friendly(true);
488 :
489 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
490 : basic_block_,
491 : AsanBasicBlockTransform::kSafeStackAccess,
492 E : BlockGraph::PE_IMAGE));
493 :
494 : // Ensure this basic block is instrumented.
495 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
496 E : uint32_t after_instructions_count = basic_block_->instructions().size();
497 E : ASSERT_LT(before_instructions_count, after_instructions_count);
498 :
499 : // Walk through the instructions and validate the source range.
500 : BasicBlock::Instructions::const_iterator iter_inst =
501 E : basic_block_->instructions().begin();
502 :
503 E : for ( ; iter_inst != basic_block_->instructions().end(); ++iter_inst)
504 E : EXPECT_EQ(source_range, iter_inst->source_range());
505 E : }
506 :
507 E : TEST_F(AsanTransformTest, InjectAsanHooksCoff) {
508 : // Add a read access to the memory.
509 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ebx));
510 : // Add a write access to the memory.
511 E : bb_asm_->mov(block_graph::Operand(assm::ecx), assm::edx);
512 :
513 : // Add source ranges to the instruction.
514 E : block_graph::Instruction& i1 = *basic_block_->instructions().begin();
515 : Instruction::SourceRange source_range =
516 E : Instruction::SourceRange(RelativeAddress(1000), i1.size());
517 E : i1.set_source_range(source_range);
518 :
519 : // Instrument this basic block.
520 E : InitHooksRefs();
521 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
522 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
523 : basic_block_,
524 : AsanBasicBlockTransform::kSafeStackAccess,
525 E : BlockGraph::COFF_IMAGE));
526 :
527 : // Ensure that the basic block is instrumented.
528 :
529 : // Check what the transform reports at first.
530 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
531 :
532 : // We had 2 instructions initially, and for each of them we add 3
533 : // instructions, so we expect to have 2 + 3 * 2 = 8 instructions.
534 E : ASSERT_EQ(basic_block_->instructions().size(), 8);
535 :
536 : // Walk through the instructions to ensure that the Asan hooks have been
537 : // injected.
538 : BasicBlock::Instructions::const_iterator iter_inst =
539 E : basic_block_->instructions().begin();
540 :
541 E : Instruction::SourceRange empty_source_range;
542 E : ASSERT_NE(empty_source_range, source_range);
543 :
544 : // First we check if the first memory access is instrumented as a 4 byte read
545 : // access. We also validate that the instrumentation has not had source range
546 : // information added.
547 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
548 E : ASSERT_EQ(I_PUSH, (iter_inst++)->representation().opcode);
549 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
550 E : ASSERT_EQ(I_LEA, (iter_inst++)->representation().opcode);
551 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
552 E : ASSERT_EQ(iter_inst->references().size(), 1);
553 : HookMapEntryKey check_4_byte_read_key =
554 E : { AsanBasicBlockTransform::kReadAccess, 4, 0, true };
555 E : ASSERT_EQ(hooks_check_access_[check_4_byte_read_key],
556 E : iter_inst->references().begin()->second.block());
557 E : ASSERT_EQ(O_PC, iter_inst->representation().ops[0].type);
558 E : ASSERT_EQ(I_CALL, (iter_inst++)->representation().opcode);
559 E : ASSERT_EQ(I_MOV, (iter_inst++)->representation().opcode);
560 :
561 : // Then we check if the second memory access is well instrumented as a 4 byte
562 : // write access.
563 E : ASSERT_EQ(I_PUSH, (iter_inst++)->representation().opcode);
564 E : ASSERT_EQ(I_LEA, (iter_inst++)->representation().opcode);
565 E : ASSERT_EQ(iter_inst->references().size(), 1);
566 : HookMapEntryKey check_4_byte_write_key =
567 E : { AsanBasicBlockTransform::kWriteAccess, 4, 0, true };
568 E : ASSERT_EQ(hooks_check_access_[check_4_byte_write_key],
569 E : iter_inst->references().begin()->second.block());
570 E : ASSERT_EQ(I_CALL, (iter_inst++)->representation().opcode);
571 E : ASSERT_EQ(I_MOV, (iter_inst++)->representation().opcode);
572 :
573 E : ASSERT_TRUE(iter_inst == basic_block_->instructions().end());
574 E : }
575 :
576 E : TEST_F(AsanTransformTest, InstrumentDifferentKindOfInstructions) {
577 E : uint32_t instrumentable_instructions = 0;
578 :
579 : // Generate a bunch of instrumentable and non-instrumentable instructions.
580 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ebx));
581 E : instrumentable_instructions++;
582 E : bb_asm_->mov(block_graph::Operand(assm::ecx), assm::edx);
583 E : instrumentable_instructions++;
584 E : bb_asm_->call(block_graph::Operand(assm::ecx));
585 E : instrumentable_instructions++;
586 E : bb_asm_->jmp(block_graph::Operand(assm::ecx));
587 E : instrumentable_instructions++;
588 E : bb_asm_->push(block_graph::Operand(assm::eax));
589 E : instrumentable_instructions++;
590 :
591 : // Non-instrumentable.
592 E : bb_asm_->lea(assm::eax, block_graph::Operand(assm::ecx));
593 :
594 : uint32_t expected_instructions_count =
595 E : basic_block_->instructions().size() + 3 * instrumentable_instructions;
596 : // Instrument this basic block.
597 E : InitHooksRefs();
598 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
599 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
600 : basic_block_,
601 : AsanBasicBlockTransform::kSafeStackAccess,
602 E : BlockGraph::PE_IMAGE));
603 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
604 E : ASSERT_EQ(basic_block_->instructions().size(), expected_instructions_count);
605 E : }
606 :
607 E : TEST_F(AsanTransformTest, InstrumentAndRemoveRedundantChecks) {
608 E : uint32_t instrumentable_instructions = 0;
609 :
610 : // Generate a bunch of instrumentable and non instrumentable instructions.
611 : // We generate operand [ecx] multiple time as a redundant memory access.
612 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ecx));
613 E : instrumentable_instructions++;
614 E : bb_asm_->mov(block_graph::Operand(assm::ecx), assm::edx);
615 : // Validate that indirect call clear the memory state.
616 E : bb_asm_->call(block_graph::Operand(assm::ecx));
617 E : bb_asm_->push(block_graph::Operand(assm::eax));
618 E : instrumentable_instructions++;
619 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ecx));
620 E : instrumentable_instructions++;
621 E : bb_asm_->jmp(block_graph::Operand(assm::ecx));
622 :
623 : uint32_t expected_instructions_count =
624 E : basic_block_->instructions().size() + 3 * instrumentable_instructions;
625 : // Instrument this basic block.
626 E : InitHooksRefs();
627 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
628 E : bb_transform.set_remove_redundant_checks(true);
629 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
630 : basic_block_,
631 : AsanBasicBlockTransform::kSafeStackAccess,
632 E : BlockGraph::PE_IMAGE));
633 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
634 E : ASSERT_EQ(basic_block_->instructions().size(), expected_instructions_count);
635 E : }
636 :
637 E : TEST_F(AsanTransformTest, NonInstrumentableStackBasedInstructions) {
638 : // DEC DWORD [EBP - 0x2830]
639 : static const uint8_t kDec1[6] = {0xff, 0x8d, 0xd0, 0xd7, 0xff, 0xff};
640 : // INC DWORD [EBP - 0x31c]
641 : static const uint8_t kInc1[6] = {0xff, 0x85, 0xe4, 0xfc, 0xff, 0xff};
642 : // INC DWORD [ESP + 0x1c]
643 : static const uint8_t kInc2[4] = {0xff, 0x44, 0x24, 0x1c};
644 : // NEG DWORD [EBP + 0x24]
645 : static const uint8_t kNeg1[3] = {0xf7, 0x5d, 0x24};
646 : // FILD QWORD [EBP - 0x8]
647 : static const uint8_t kFild1[3] = {0xdf, 0x6d, 0xf8};
648 : // FISTP QWORD [ESP + 0x28]
649 : static const uint8_t kFistp1[4] = {0xdf, 0x7c, 0x24, 0x28};
650 : // MOV EDI, [EBP - 0x4]
651 : static const uint8_t kMov1[3] = {0x8b, 0x7d, 0xfc};
652 : // MOV EAX, [EBP - 0x104]
653 : static const uint8_t kMov2[6] = {0x8b, 0x85, 0xfc, 0xfe, 0xff, 0xff};
654 :
655 E : ASSERT_TRUE(AddInstructionFromBuffer(kDec1, sizeof(kDec1)));
656 E : ASSERT_TRUE(AddInstructionFromBuffer(kInc1, sizeof(kInc1)));
657 E : ASSERT_TRUE(AddInstructionFromBuffer(kInc2, sizeof(kInc2)));
658 E : ASSERT_TRUE(AddInstructionFromBuffer(kNeg1, sizeof(kNeg1)));
659 E : ASSERT_TRUE(AddInstructionFromBuffer(kFild1, sizeof(kFild1)));
660 E : ASSERT_TRUE(AddInstructionFromBuffer(kFistp1, sizeof(kFistp1)));
661 E : ASSERT_TRUE(AddInstructionFromBuffer(kMov1, sizeof(kMov1)));
662 E : ASSERT_TRUE(AddInstructionFromBuffer(kMov2, sizeof(kMov2)));
663 :
664 : // Keep track of the basic block size before Asan transform.
665 E : uint32_t expected_basic_block_size = basic_block_->instructions().size();
666 :
667 : // Instrument this basic block.
668 E : InitHooksRefs();
669 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
670 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
671 : basic_block_,
672 : AsanBasicBlockTransform::kSafeStackAccess,
673 E : BlockGraph::PE_IMAGE));
674 :
675 : // Non-instrumentable instructions implies no change.
676 E : EXPECT_FALSE(bb_transform.instrumentation_happened());
677 E : EXPECT_EQ(expected_basic_block_size, basic_block_->instructions().size());
678 E : }
679 :
680 E : TEST_F(AsanTransformTest, InstrumentableStackBasedUnsafeInstructions) {
681 : // DEC DWORD [EBP - 0x2830]
682 : static const uint8_t kDec1[6] = {0xff, 0x8d, 0xd0, 0xd7, 0xff, 0xff};
683 :
684 E : ASSERT_TRUE(AddInstructionFromBuffer(kDec1, sizeof(kDec1)));
685 :
686 : // Keep track of the basic block size before Asan transform.
687 E : uint32_t previous_basic_block_size = basic_block_->instructions().size();
688 :
689 : // Instrument this basic block considering invalid stack manipulation.
690 E : InitHooksRefs();
691 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
692 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
693 : basic_block_,
694 : AsanBasicBlockTransform::kUnsafeStackAccess,
695 E : BlockGraph::PE_IMAGE));
696 :
697 : // This instruction should have been instrumented, and we must observe
698 : // a increase in size.
699 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
700 E : EXPECT_LT(previous_basic_block_size, basic_block_->instructions().size());
701 E : }
702 :
703 E : TEST_F(AsanTransformTest, NonInstrumentableSegmentBasedInstructions) {
704 : // add eax, fs:[eax]
705 : static const uint8_t kAdd1[3] = {0x64, 0x03, 0x00};
706 : // inc gs:[eax]
707 : static const uint8_t kInc1[3] = {0x65, 0xFE, 0x00};
708 :
709 E : ASSERT_TRUE(AddInstructionFromBuffer(kAdd1, sizeof(kAdd1)));
710 E : ASSERT_TRUE(AddInstructionFromBuffer(kInc1, sizeof(kInc1)));
711 :
712 : // Keep track of the basic block size before Asan transform.
713 E : uint32_t expected_basic_block_size = basic_block_->instructions().size();
714 :
715 : // Instrument this basic block.
716 E : InitHooksRefs();
717 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
718 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
719 : basic_block_,
720 : AsanBasicBlockTransform::kSafeStackAccess,
721 E : BlockGraph::PE_IMAGE));
722 :
723 : // Non-instrumentable instructions implies no change.
724 E : EXPECT_FALSE(bb_transform.instrumentation_happened());
725 E : EXPECT_EQ(expected_basic_block_size, basic_block_->instructions().size());
726 E : }
727 :
728 E : TEST_F(AsanTransformTest, FilteredInstructionsNotInstrumented) {
729 : // Add a read access to the memory.
730 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ebx));
731 : // Add a write access to the memory.
732 E : bb_asm_->mov(block_graph::Operand(assm::ecx), assm::edx);
733 :
734 : // Add a source range to the first instruction.
735 E : block_graph::Instruction& i1 = *basic_block_->instructions().begin();
736 E : i1.set_source_range(Instruction::SourceRange(
737 : RelativeAddress(1000), i1.size()));
738 :
739 : // Create a filter that blocks out that source range.
740 E : RelativeAddressFilter filter(
741 : RelativeAddressFilter::Range(RelativeAddress(0), 2000));
742 E : filter.Mark(RelativeAddressFilter::Range(RelativeAddress(995), 50));
743 :
744 : // Pass the filter to the BB transform.
745 E : InitHooksRefs();
746 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
747 E : bb_transform.set_filter(&filter);
748 :
749 : // Instrument this basic block.
750 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
751 : basic_block_,
752 : AsanBasicBlockTransform::kSafeStackAccess,
753 E : BlockGraph::PE_IMAGE));
754 :
755 : // Ensure that the basic block is instrumented, but only the second
756 : // instruction.
757 :
758 : // The transform should report changes.
759 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
760 :
761 : // We had 2 instructions initially. For the second one we add 3
762 : // instructions, so we expect to have 1 + (1 + 3) = 5 instructions.
763 E : ASSERT_EQ(basic_block_->instructions().size(), 5);
764 :
765 : // Walk through the instructions to ensure that the Asan hooks have been
766 : // injected.
767 : BasicBlock::Instructions::const_iterator iter_inst =
768 E : basic_block_->instructions().begin();
769 :
770 : // Ensure the first instruction is not instrumented at all.
771 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_MOV);
772 :
773 : // Then we check if the second memory access is well instrumented as a 4 byte
774 : // write access.
775 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_PUSH);
776 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_LEA);
777 E : ASSERT_EQ(iter_inst->references().size(), 1);
778 : HookMapEntryKey check_4_byte_write_key =
779 E : { AsanBasicBlockTransform::kWriteAccess, 4, 0, true };
780 E : ASSERT_TRUE(iter_inst->references().begin()->second.block()
781 E : == hooks_check_access_[check_4_byte_write_key]);
782 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_CALL);
783 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_MOV);
784 :
785 E : ASSERT_TRUE(iter_inst == basic_block_->instructions().end());
786 E : }
787 :
788 E : TEST_F(AsanTransformTest, InstrumentableStringInstructions) {
789 : static const uint8_t movsd[1] = {0xA5};
790 : static const uint8_t movsw[2] = {0x66, 0xA5};
791 : static const uint8_t movsb[1] = {0xA4};
792 :
793 : static const uint8_t cmpsd[1] = {0xA7};
794 : static const uint8_t cmpsw[2] = {0x66, 0xA7};
795 : static const uint8_t cmpsb[1] = {0xA6};
796 :
797 : static const uint8_t stosd[1] = {0xAB};
798 : static const uint8_t stosw[2] = {0x66, 0xAB};
799 : static const uint8_t stosb[1] = {0xAA};
800 :
801 E : EXPECT_TRUE(AddInstructionFromBuffer(movsd, sizeof(movsd)));
802 E : EXPECT_TRUE(AddInstructionFromBuffer(movsw, sizeof(movsw)));
803 E : EXPECT_TRUE(AddInstructionFromBuffer(movsb, sizeof(movsb)));
804 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsd, sizeof(cmpsd)));
805 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsw, sizeof(cmpsw)));
806 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsb, sizeof(cmpsb)));
807 E : EXPECT_TRUE(AddInstructionFromBuffer(stosd, sizeof(stosd)));
808 E : EXPECT_TRUE(AddInstructionFromBuffer(stosw, sizeof(stosw)));
809 E : EXPECT_TRUE(AddInstructionFromBuffer(stosb, sizeof(stosb)));
810 :
811 : // Keep number of instrumentable instructions.
812 E : uint32_t count_instructions = basic_block_->instructions().size();
813 :
814 : // Keep track of the basic block size before Asan transform.
815 E : uint32_t basic_block_size = basic_block_->instructions().size();
816 :
817 : // Instrument this basic block.
818 E : InitHooksRefs();
819 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
820 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
821 : basic_block_,
822 : AsanBasicBlockTransform::kSafeStackAccess,
823 E : BlockGraph::PE_IMAGE));
824 :
825 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
826 :
827 : // Each instrumentable instructions implies 1 new instructions.
828 E : uint32_t expected_basic_block_size = count_instructions + basic_block_size;
829 :
830 : // Validate basic block size.
831 E : ASSERT_EQ(basic_block_->instructions().size(), expected_basic_block_size);
832 E : }
833 :
834 E : TEST_F(AsanTransformTest, InstrumentableRepzStringInstructions) {
835 : static const uint8_t movsd[2] = {0xF3, 0xA5};
836 : static const uint8_t movsw[3] = {0xF3, 0x66, 0xA5};
837 : static const uint8_t movsb[2] = {0xF3, 0xA4};
838 :
839 : static const uint8_t cmpsd[2] = {0xF3, 0xA7};
840 : static const uint8_t cmpsw[3] = {0xF3, 0x66, 0xA7};
841 : static const uint8_t cmpsb[2] = {0xF3, 0xA6};
842 :
843 : static const uint8_t stosd[2] = {0xF3, 0xAB};
844 : static const uint8_t stosw[3] = {0xF3, 0x66, 0xAB};
845 : static const uint8_t stosb[2] = {0xF3, 0xAA};
846 :
847 E : EXPECT_TRUE(AddInstructionFromBuffer(movsd, sizeof(movsd)));
848 E : EXPECT_TRUE(AddInstructionFromBuffer(movsw, sizeof(movsw)));
849 E : EXPECT_TRUE(AddInstructionFromBuffer(movsb, sizeof(movsb)));
850 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsd, sizeof(cmpsd)));
851 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsw, sizeof(cmpsw)));
852 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsb, sizeof(cmpsb)));
853 E : EXPECT_TRUE(AddInstructionFromBuffer(stosd, sizeof(stosd)));
854 E : EXPECT_TRUE(AddInstructionFromBuffer(stosw, sizeof(stosw)));
855 E : EXPECT_TRUE(AddInstructionFromBuffer(stosb, sizeof(stosb)));
856 :
857 : // Keep number of instrumentable instructions.
858 E : uint32_t count_instructions = basic_block_->instructions().size();
859 :
860 : // Keep track of the basic block size before Asan transform.
861 E : uint32_t basic_block_size = basic_block_->instructions().size();
862 :
863 : // Instrument this basic block.
864 E : InitHooksRefs();
865 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
866 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
867 : basic_block_,
868 : AsanBasicBlockTransform::kSafeStackAccess,
869 E : BlockGraph::PE_IMAGE));
870 :
871 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
872 :
873 : // Each instrumentable instructions implies 1 new instructions.
874 E : uint32_t expected_basic_block_size = count_instructions + basic_block_size;
875 :
876 : // Validate basic block size.
877 E : ASSERT_EQ(basic_block_->instructions().size(), expected_basic_block_size);
878 E : }
879 :
880 E : TEST_F(AsanTransformTest, DryRunInstrumentable) {
881 : // Generate an instrumentable instruction.
882 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ecx));
883 :
884 : // Keep track of the basic block size before Asan transform.
885 E : uint32_t basic_block_size = basic_block_->instructions().size();
886 :
887 : // Instrument this basic block.
888 : // Note that InitHooksRefs() is not needed for a dry run.
889 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
890 E : bb_transform.set_dry_run(true);
891 :
892 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
893 : basic_block_,
894 : AsanBasicBlockTransform::kSafeStackAccess,
895 E : BlockGraph::PE_IMAGE));
896 :
897 : // The instructions_happened() function should return true because we
898 : // had an instruction to instrument.
899 E : EXPECT_TRUE(bb_transform.instrumentation_happened());
900 :
901 : // Yet, the basic block should not be touched.
902 E : ASSERT_EQ(basic_block_size, basic_block_->instructions().size());
903 E : }
904 :
905 E : TEST_F(AsanTransformTest, DryRunNonInstrumentable) {
906 : // Generate a non-instrumentable instruction.
907 E : bb_asm_->xchg(assm::eax, assm::ecx);
908 :
909 : // Keep track of the basic block size before Asan transform.
910 E : uint32_t basic_block_size = basic_block_->instructions().size();
911 :
912 : // Instrument this basic block.
913 : // Note that InitHooksRefs() is not needed for a dry run.
914 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
915 E : bb_transform.set_dry_run(true);
916 :
917 E : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
918 : basic_block_,
919 : AsanBasicBlockTransform::kSafeStackAccess,
920 E : BlockGraph::PE_IMAGE));
921 :
922 : // The instructions_happened() function should return false because we
923 : // had no instructions to instrument.
924 E : EXPECT_FALSE(bb_transform.instrumentation_happened());
925 :
926 : // The basic block should not be touched either.
927 E : ASSERT_EQ(basic_block_size, basic_block_->instructions().size());
928 E : }
929 :
930 E : TEST_F(AsanTransformTest, HotPatchingBBTransformInstrumentable) {
931 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
932 E : bb_transform.set_dry_run(true);
933 E : HotPatchingAsanBasicBlockTransform hp_bb_transform(&bb_transform);
934 :
935 : // Generate an instrumentable basic block.
936 E : bb_asm_->xchg(assm::eax, assm::ecx);
937 E : bb_asm_->mov(assm::eax, block_graph::Operand(assm::ebx));
938 :
939 : // Check subgraph before transformation.
940 E : EXPECT_EQ(2U, basic_block_->instructions().size());
941 E : ASSERT_EQ(1U, subgraph_.block_descriptions().size());
942 E : EXPECT_EQ(0U, subgraph_.block_descriptions().front().padding_before);
943 E : EXPECT_FALSE(hp_bb_transform.prepared_for_hot_patching());
944 :
945 : // Apply the Asan hot patching basic block transform.
946 E : ASSERT_TRUE(hp_bb_transform.TransformBasicBlockSubGraph(
947 E : &pe_policy_, &block_graph_, &subgraph_));
948 :
949 : // Padding should be added in the block description. Also, as the code block
950 : // began with a 1-byte instruction, a 2-byte NOP should be prepended.
951 E : EXPECT_EQ(3U, basic_block_->instructions().size());
952 E : ASSERT_EQ(1U, subgraph_.block_descriptions().size());
953 E : EXPECT_EQ(5U, subgraph_.block_descriptions().front().padding_before);
954 E : EXPECT_TRUE(hp_bb_transform.prepared_for_hot_patching());
955 E : }
956 :
957 E : TEST_F(AsanTransformTest, HotPatchingBBTransformNonInstrumentable) {
958 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
959 E : bb_transform.set_dry_run(true);
960 E : HotPatchingAsanBasicBlockTransform hp_bb_transform(&bb_transform);
961 :
962 : // Generate a non-instrumentable instruction.
963 E : bb_asm_->xchg(assm::eax, assm::ecx);
964 :
965 : // Check subgraph before transformation.
966 E : EXPECT_EQ(1U, basic_block_->instructions().size());
967 E : ASSERT_EQ(1U, subgraph_.block_descriptions().size());
968 E : EXPECT_EQ(0U, subgraph_.block_descriptions().front().padding_before);
969 E : EXPECT_FALSE(hp_bb_transform.prepared_for_hot_patching());
970 :
971 : // Apply the Asan hot patching basic block transform.
972 E : ASSERT_TRUE(hp_bb_transform.TransformBasicBlockSubGraph(
973 E : &pe_policy_, &block_graph_, &subgraph_));
974 :
975 : // The subgraph should stay the same.
976 E : EXPECT_EQ(1U, basic_block_->instructions().size());
977 E : ASSERT_EQ(1U, subgraph_.block_descriptions().size());
978 E : EXPECT_EQ(0U, subgraph_.block_descriptions().front().padding_before);
979 E : EXPECT_FALSE(hp_bb_transform.prepared_for_hot_patching());
980 E : }
981 :
982 : namespace {
983 :
984 : using base::win::PEImage;
985 : typedef std::set<std::string> StringSet;
986 : typedef std::set<void*> FunctionsIATAddressSet;
987 : typedef std::vector<std::string> StringVector;
988 :
989 E : void Intersect(const StringSet& ss1, const StringSet& ss2, StringSet* ss3) {
990 E : ASSERT_TRUE(ss3 != NULL);
991 E : ss3->clear();
992 E : std::set_intersection(ss1.begin(), ss1.end(),
993 : ss2.begin(), ss2.end(),
994 : std::inserter(*ss3, ss3->begin()));
995 E : }
996 :
997 : bool EnumKernel32HeapImports(const PEImage &image,
998 : const char* module,
999 : unsigned long ordinal,
1000 : const char* name,
1001 : unsigned long hint,
1002 : PIMAGE_THUNK_DATA iat,
1003 E : void* cookie) {
1004 E : EXPECT_NE(static_cast<const char*>(NULL), module);
1005 E : EXPECT_NE(static_cast<void*>(NULL), cookie);
1006 :
1007 E : StringVector* modules = reinterpret_cast<StringVector*>(cookie);
1008 :
1009 E : if (_stricmp("kernel32.dll", module) == 0 && strncmp("Heap", name, 4) == 0) {
1010 E : EXPECT_NE(static_cast<const char*>(NULL), name);
1011 E : modules->push_back(name);
1012 : }
1013 :
1014 E : return true;
1015 E : }
1016 :
1017 : bool EnumKernel32InterceptedFunctionsImports(const PEImage &image,
1018 : const char* module,
1019 : unsigned long ordinal,
1020 : const char* name,
1021 : unsigned long hint,
1022 : PIMAGE_THUNK_DATA iat,
1023 E : void* cookie) {
1024 E : EXPECT_NE(static_cast<const char*>(NULL), module);
1025 E : EXPECT_NE(static_cast<void*>(NULL), cookie);
1026 :
1027 E : StringVector* modules = reinterpret_cast<StringVector*>(cookie);
1028 : static const char* kInterceptedFunctions[] = {
1029 : "ReadFile",
1030 : "WriteFile",
1031 : };
1032 :
1033 E : if (_stricmp("kernel32.dll", module) == 0) {
1034 E : for (size_t i = 0; i < arraysize(kInterceptedFunctions); ++i) {
1035 E : if (base::CompareCaseInsensitiveASCII(kInterceptedFunctions[i], name) ==
1036 : 0) {
1037 E : EXPECT_NE(static_cast<const char*>(NULL), name);
1038 E : modules->push_back(name);
1039 E : return true;
1040 : }
1041 E : }
1042 : }
1043 :
1044 E : return true;
1045 E : }
1046 :
1047 : // A struct used to pass parameters in the cookie of EnumAsanImports
1048 : struct EnumAsanImportsParams {
1049 : const char* imported_module_name;
1050 : StringSet* imports;
1051 : };
1052 :
1053 : bool EnumAsanImports(const PEImage &image,
1054 : const char* module,
1055 : unsigned long ordinal,
1056 : const char* name,
1057 : unsigned long hint,
1058 : PIMAGE_THUNK_DATA iat,
1059 E : void* cookie) {
1060 E : EXPECT_NE(static_cast<const char*>(NULL), module);
1061 E : EXPECT_NE(static_cast<void*>(NULL), cookie);
1062 :
1063 : EnumAsanImportsParams* params =
1064 E : reinterpret_cast<EnumAsanImportsParams*>(cookie);
1065 :
1066 E : if (strcmp(params->imported_module_name, module) == 0) {
1067 E : EXPECT_NE(static_cast<const char*>(NULL), name);
1068 E : params->imports->insert(name);
1069 : }
1070 :
1071 E : return true;
1072 E : }
1073 :
1074 : bool GetAsanHooksIATEntries(const PEImage &image,
1075 : const char* module,
1076 : unsigned long ordinal,
1077 : const char* name,
1078 : unsigned long hint,
1079 : PIMAGE_THUNK_DATA iat,
1080 E : void* cookie) {
1081 E : EXPECT_NE(static_cast<const char*>(NULL), module);
1082 E : EXPECT_NE(static_cast<void*>(NULL), cookie);
1083 :
1084 : FunctionsIATAddressSet* hooks_iat_entries =
1085 E : reinterpret_cast<FunctionsIATAddressSet*>(cookie);
1086 :
1087 E : if (strcmp(AsanTransform::kSyzyAsanDll, module) != 0)
1088 E : return true;
1089 :
1090 E : EXPECT_NE(static_cast<const char*>(NULL), name);
1091 :
1092 : // Ensures that the function is an asan_check_access hook.
1093 E : if (base::StartsWith(name, "asan_check_", base::CompareCase::SENSITIVE))
1094 E : hooks_iat_entries->insert(reinterpret_cast<PVOID>(iat->u1.Function));
1095 :
1096 E : return true;
1097 E : }
1098 :
1099 : void CheckImportsAreRedirectedPe(
1100 : const base::FilePath::StringType& library_path,
1101 E : bool hot_patching) {
1102 :
1103 : // Load the transformed module without resolving its dependencies.
1104 : base::NativeLibrary lib =
1105 E : ::LoadLibraryEx(library_path.c_str(),
1106 : NULL,
1107 : DONT_RESOLVE_DLL_REFERENCES);
1108 E : ASSERT_TRUE(lib != NULL);
1109 : // Make sure it's unloaded on failure.
1110 E : base::ScopedNativeLibrary lib_keeper(lib);
1111 :
1112 E : PEImage image(lib);
1113 E : ASSERT_TRUE(image.VerifyMagic());
1114 E : StringSet imports;
1115 : EnumAsanImportsParams enum_asan_imports_params;
1116 E : if (!hot_patching) {
1117 E : enum_asan_imports_params.imported_module_name = AsanTransform::kSyzyAsanDll;
1118 E : } else {
1119 E : enum_asan_imports_params.imported_module_name =
1120 : AsanTransform::kSyzyAsanHpDll;
1121 : }
1122 E : enum_asan_imports_params.imports = &imports;
1123 E : ASSERT_TRUE(image.EnumAllImports(&EnumAsanImports,
1124 E : &enum_asan_imports_params));
1125 :
1126 E : StringVector heap_imports;
1127 E : ASSERT_TRUE(image.EnumAllImports(&EnumKernel32HeapImports, &heap_imports));
1128 E : StringVector intercepted_functions_imports;
1129 E : ASSERT_TRUE(image.EnumAllImports(&EnumKernel32InterceptedFunctionsImports,
1130 E : &intercepted_functions_imports));
1131 :
1132 : // This isn't strictly speaking a full test, as we only check that the new
1133 : // imports have been added. It's however more trouble than it's worth to
1134 : // test this fully for now.
1135 E : StringSet expected;
1136 E : for (size_t i = 0; i < heap_imports.size(); ++i) {
1137 E : std::string asan_import = "asan_";
1138 E : asan_import.append(heap_imports[i]);
1139 E : expected.insert(asan_import);
1140 E : }
1141 E : for (size_t i = 0; i < intercepted_functions_imports.size(); ++i) {
1142 E : std::string asan_import = "asan_";
1143 E : asan_import.append(intercepted_functions_imports[i]);
1144 E : expected.insert(asan_import);
1145 E : }
1146 :
1147 : // Imports that should be redirected should all have matching asan imports.
1148 E : StringSet results;
1149 E : Intersect(imports, expected, &results);
1150 E : if (!hot_patching) {
1151 E : EXPECT_EQ(expected, results);
1152 E : } else {
1153 : // These should not be present in hot patching mode.
1154 E : EXPECT_TRUE(results.empty());
1155 : }
1156 :
1157 : // Some instrumentation functions (but not necessarily all of them) should be
1158 : // found, unless we are in hot patching mode.
1159 E : expected.clear();
1160 E : expected.insert("asan_check_1_byte_read_access");
1161 E : expected.insert("asan_check_2_byte_read_access");
1162 E : expected.insert("asan_check_4_byte_read_access");
1163 E : expected.insert("asan_check_8_byte_read_access");
1164 E : expected.insert("asan_check_10_byte_read_access");
1165 E : expected.insert("asan_check_16_byte_read_access");
1166 E : expected.insert("asan_check_32_byte_read_access");
1167 E : expected.insert("asan_check_1_byte_write_access");
1168 E : expected.insert("asan_check_2_byte_write_access");
1169 E : expected.insert("asan_check_4_byte_write_access");
1170 E : expected.insert("asan_check_8_byte_write_access");
1171 E : expected.insert("asan_check_10_byte_write_access");
1172 E : expected.insert("asan_check_16_byte_write_access");
1173 E : expected.insert("asan_check_32_byte_write_access");
1174 :
1175 E : expected.insert("asan_check_1_byte_read_access_no_flags");
1176 E : expected.insert("asan_check_2_byte_read_access_no_flags");
1177 E : expected.insert("asan_check_4_byte_read_access_no_flags");
1178 E : expected.insert("asan_check_8_byte_read_access_no_flags");
1179 E : expected.insert("asan_check_10_byte_read_access_no_flags");
1180 E : expected.insert("asan_check_16_byte_read_access_no_flags");
1181 E : expected.insert("asan_check_32_byte_read_access_no_flags");
1182 E : expected.insert("asan_check_1_byte_write_access_no_flags");
1183 E : expected.insert("asan_check_2_byte_write_access_no_flags");
1184 E : expected.insert("asan_check_4_byte_write_access_no_flags");
1185 E : expected.insert("asan_check_8_byte_write_access_no_flags");
1186 E : expected.insert("asan_check_10_byte_write_access_no_flags");
1187 E : expected.insert("asan_check_16_byte_write_access_no_flags");
1188 E : expected.insert("asan_check_32_byte_write_access_no_flags");
1189 :
1190 E : expected.insert("asan_check_repz_4_byte_cmps_access");
1191 E : expected.insert("asan_check_repz_4_byte_movs_access");
1192 E : expected.insert("asan_check_repz_4_byte_stos_access");
1193 E : expected.insert("asan_check_repz_2_byte_cmps_access");
1194 E : expected.insert("asan_check_repz_2_byte_movs_access");
1195 E : expected.insert("asan_check_repz_2_byte_stos_access");
1196 E : expected.insert("asan_check_repz_1_byte_cmps_access");
1197 E : expected.insert("asan_check_repz_1_byte_movs_access");
1198 E : expected.insert("asan_check_repz_1_byte_stos_access");
1199 :
1200 E : expected.insert("asan_check_4_byte_cmps_access");
1201 E : expected.insert("asan_check_4_byte_movs_access");
1202 E : expected.insert("asan_check_4_byte_stos_access");
1203 E : expected.insert("asan_check_2_byte_cmps_access");
1204 E : expected.insert("asan_check_2_byte_movs_access");
1205 E : expected.insert("asan_check_2_byte_stos_access");
1206 E : expected.insert("asan_check_1_byte_cmps_access");
1207 E : expected.insert("asan_check_1_byte_movs_access");
1208 E : expected.insert("asan_check_1_byte_stos_access");
1209 :
1210 : // We expect all of the instrumentation functions to have been added.
1211 E : Intersect(imports, expected, &results);
1212 E : if (!hot_patching) {
1213 E : EXPECT_EQ(expected, results);
1214 E : } else {
1215 : // These should not be present in hot patching mode.
1216 E : EXPECT_TRUE(results.empty());
1217 : }
1218 :
1219 : // Hot patching mode uses a different prefix for static CRT intercepts.
1220 E : std::string prefix = !hot_patching ? "asan_" : "hp_asan_";
1221 :
1222 : // We expect all of these statically linked CRT functions to be redirected.
1223 E : expected.clear();
1224 E : expected.insert(prefix + "memcpy");
1225 E : expected.insert(prefix + "memmove");
1226 E : expected.insert(prefix + "memset");
1227 E : expected.insert(prefix + "memchr");
1228 E : expected.insert(prefix + "strlen");
1229 E : expected.insert(prefix + "strrchr");
1230 E : expected.insert(prefix + "strncpy");
1231 E : expected.insert(prefix + "strncat");
1232 E : expected.insert(prefix + "wcsrchr");
1233 E : expected.insert(prefix + "wcschr");
1234 E : expected.insert(prefix + "wcsstr");
1235 E : Intersect(imports, expected, &results);
1236 E : EXPECT_FALSE(results.empty());
1237 E : EXPECT_EQ(results, expected);
1238 :
1239 : // The implementation of the interceptors for these functions isn't available
1240 : // so we don't expect them to be redirected.
1241 E : StringSet not_expected;
1242 E : not_expected.insert(prefix + "strcmp");
1243 E : not_expected.insert(prefix + "strcspn");
1244 E : not_expected.insert(prefix + "strspn");
1245 E : not_expected.insert(prefix + "strstr");
1246 E : not_expected.insert(prefix + "strpbrk");
1247 E : Intersect(imports, not_expected, &results);
1248 E : EXPECT_TRUE(results.empty());
1249 E : }
1250 :
1251 : } // namespace
1252 :
1253 E : TEST_F(AsanTransformTest, ImportsAreRedirectedPe) {
1254 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformToIntegrationTestDll());
1255 :
1256 E : CheckImportsAreRedirectedPe(relinked_path_.value(), false);
1257 E : }
1258 :
1259 E : TEST_F(AsanTransformTest, ImportsAreRedirectedHpPe) {
1260 E : asan_transform_.set_hot_patching(true);
1261 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformToIntegrationTestDll());
1262 :
1263 E : CheckImportsAreRedirectedPe(relinked_path_.value(), true);
1264 E : }
1265 :
1266 : namespace {
1267 :
1268 : // Counts the number of references to the given COFF symbol.
1269 : size_t CountCoffSymbolReferences(const BlockGraph::Block* symbols_block,
1270 : const pe::CoffSymbolNameOffsetMap& symbol_map,
1271 E : const base::StringPiece& name) {
1272 E : EXPECT_NE(reinterpret_cast<BlockGraph::Block*>(NULL), symbols_block);
1273 :
1274 : pe::CoffSymbolNameOffsetMap::const_iterator symbol_it =
1275 E : symbol_map.find(name.as_string());
1276 E : if (symbol_it == symbol_map.end())
1277 i : return 0;
1278 :
1279 E : size_t ref_count = 0;
1280 E : const pe::CoffSymbolOffsets& offsets = symbol_it->second;
1281 : BlockGraph::Block::ReferrerSet::const_iterator ref_it =
1282 E : symbols_block->referrers().begin();
1283 E : for (; ref_it != symbols_block->referrers().end(); ++ref_it) {
1284 E : BlockGraph::Reference ref;
1285 E : CHECK(ref_it->first->GetReference(ref_it->second, &ref));
1286 E : if (offsets.count(ref.offset()) > 0)
1287 E : ++ref_count;
1288 E : }
1289 :
1290 E : return ref_count;
1291 E : }
1292 :
1293 : } // namespace
1294 :
1295 E : TEST_F(AsanTransformTest, ImportsAreRedirectedCoff) {
1296 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDllObj());
1297 :
1298 : // TODO(chrisha): Modify this to use CoffTransformPolicy once it is
1299 : // working as intended.
1300 E : testing::DummyTransformPolicy dummy_policy;
1301 E : asan_transform_.use_interceptors_ = true;
1302 E : asan_transform_.use_liveness_analysis_ = true;
1303 E : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
1304 E : &asan_transform_, &dummy_policy, &block_graph_, header_block_));
1305 :
1306 E : BlockGraph::Block* symbols_block = NULL;
1307 E : BlockGraph::Block* strings_block = NULL;
1308 E : ASSERT_TRUE(pe::FindCoffSpecialBlocks(
1309 E : &block_graph_, NULL, &symbols_block, &strings_block));
1310 E : pe::CoffSymbolNameOffsetMap symbol_map;
1311 E : ASSERT_TRUE(pe::BuildCoffSymbolNameOffsetMap(
1312 E : symbols_block, strings_block, &symbol_map));
1313 :
1314 : // Convert the symbol map to a set of symbol names.
1315 E : StringSet symbols;
1316 E : pe::CoffSymbolNameOffsetMap::const_iterator map_it = symbol_map.begin();
1317 E : for (; map_it != symbol_map.end(); ++map_it)
1318 E : symbols.insert(map_it->first);
1319 :
1320 : // We expected the following check-access functions to have been
1321 : // added.
1322 E : StringSet expected;
1323 E : expected.insert("_asan_check_1_byte_read_access");
1324 E : expected.insert("_asan_check_2_byte_read_access");
1325 E : expected.insert("_asan_check_4_byte_read_access");
1326 E : expected.insert("_asan_check_8_byte_read_access");
1327 E : expected.insert("_asan_check_10_byte_read_access");
1328 E : expected.insert("_asan_check_16_byte_read_access");
1329 E : expected.insert("_asan_check_32_byte_read_access");
1330 E : expected.insert("_asan_check_1_byte_write_access");
1331 E : expected.insert("_asan_check_2_byte_write_access");
1332 E : expected.insert("_asan_check_4_byte_write_access");
1333 E : expected.insert("_asan_check_8_byte_write_access");
1334 E : expected.insert("_asan_check_10_byte_write_access");
1335 E : expected.insert("_asan_check_16_byte_write_access");
1336 E : expected.insert("_asan_check_32_byte_write_access");
1337 :
1338 E : expected.insert("_asan_check_1_byte_read_access_no_flags");
1339 E : expected.insert("_asan_check_2_byte_read_access_no_flags");
1340 E : expected.insert("_asan_check_4_byte_read_access_no_flags");
1341 E : expected.insert("_asan_check_8_byte_read_access_no_flags");
1342 E : expected.insert("_asan_check_10_byte_read_access_no_flags");
1343 E : expected.insert("_asan_check_16_byte_read_access_no_flags");
1344 E : expected.insert("_asan_check_32_byte_read_access_no_flags");
1345 E : expected.insert("_asan_check_1_byte_write_access_no_flags");
1346 E : expected.insert("_asan_check_2_byte_write_access_no_flags");
1347 E : expected.insert("_asan_check_4_byte_write_access_no_flags");
1348 E : expected.insert("_asan_check_8_byte_write_access_no_flags");
1349 E : expected.insert("_asan_check_10_byte_write_access_no_flags");
1350 E : expected.insert("_asan_check_16_byte_write_access_no_flags");
1351 E : expected.insert("_asan_check_32_byte_write_access_no_flags");
1352 :
1353 E : expected.insert("_asan_check_repz_4_byte_cmps_access");
1354 E : expected.insert("_asan_check_repz_4_byte_movs_access");
1355 E : expected.insert("_asan_check_repz_4_byte_stos_access");
1356 E : expected.insert("_asan_check_repz_2_byte_cmps_access");
1357 E : expected.insert("_asan_check_repz_2_byte_movs_access");
1358 E : expected.insert("_asan_check_repz_2_byte_stos_access");
1359 E : expected.insert("_asan_check_repz_1_byte_cmps_access");
1360 E : expected.insert("_asan_check_repz_1_byte_movs_access");
1361 E : expected.insert("_asan_check_repz_1_byte_stos_access");
1362 :
1363 E : expected.insert("_asan_check_4_byte_cmps_access");
1364 E : expected.insert("_asan_check_4_byte_movs_access");
1365 E : expected.insert("_asan_check_4_byte_stos_access");
1366 E : expected.insert("_asan_check_2_byte_cmps_access");
1367 E : expected.insert("_asan_check_2_byte_movs_access");
1368 E : expected.insert("_asan_check_2_byte_stos_access");
1369 E : expected.insert("_asan_check_1_byte_cmps_access");
1370 E : expected.insert("_asan_check_1_byte_movs_access");
1371 E : expected.insert("_asan_check_1_byte_stos_access");
1372 :
1373 E : StringSet results;
1374 E : Intersect(symbols, expected, &results);
1375 E : EXPECT_THAT(results, ContainerEq(expected));
1376 :
1377 : // Expect at least some of the Asan instrumentation symbols to be referenced.
1378 E : size_t instrumentation_references = 0;
1379 E : StringSet::const_iterator str_it = expected.begin();
1380 E : for (; str_it != expected.end(); ++str_it) {
1381 E : instrumentation_references += CountCoffSymbolReferences(
1382 : symbols_block, symbol_map, *str_it);
1383 E : }
1384 E : EXPECT_LT(0u, instrumentation_references);
1385 :
1386 : // Expect any intercepted symbols to have no references to them if they
1387 : // are present, and expect an equivalent Asan instrumented symbol to exist
1388 : // and be referenced.
1389 E : const AsanIntercept* intercept = kAsanIntercepts;
1390 E : for (; intercept->undecorated_name != NULL; ++intercept) {
1391 E : if (intercept->decorated_name == NULL)
1392 E : continue;
1393 :
1394 E : std::string name(intercept->decorated_name);
1395 :
1396 : // Build the name of the imported version of this symbol.
1397 E : std::string imp_name(kDecoratedImportPrefix);
1398 E : imp_name += name;
1399 :
1400 : // Build the name of the Asan instrumented version of this symbol.
1401 E : std::string asan_name(kDecoratedAsanInterceptPrefix);
1402 E : asan_name += name;
1403 :
1404 : // Build the name of the Asan instrumented imported version of this symbol.
1405 E : std::string imp_asan_name(kDecoratedImportPrefix);
1406 E : imp_asan_name += name;
1407 :
1408 E : bool has_name = symbols.count(name) > 0;
1409 E : bool has_imp_name = symbols.count(imp_name) > 0;
1410 E : bool has_asan_name = symbols.count(asan_name) > 0;
1411 E : bool has_imp_asan_name = symbols.count(imp_asan_name) > 0;
1412 :
1413 E : size_t name_refs = 0;
1414 E : if (has_name) {
1415 E : name_refs = CountCoffSymbolReferences(
1416 : symbols_block, symbol_map, name);
1417 : }
1418 :
1419 E : size_t imp_name_refs = 0;
1420 E : if (has_imp_name) {
1421 i : imp_name_refs = CountCoffSymbolReferences(
1422 : symbols_block, symbol_map, imp_name);
1423 : }
1424 :
1425 E : size_t asan_name_refs = 0;
1426 E : if (has_asan_name) {
1427 E : asan_name_refs = CountCoffSymbolReferences(
1428 : symbols_block, symbol_map, asan_name);
1429 : }
1430 :
1431 E : size_t imp_asan_name_refs = 0;
1432 E : if (has_imp_asan_name) {
1433 i : imp_asan_name_refs = CountCoffSymbolReferences(
1434 : symbols_block, symbol_map, imp_asan_name);
1435 : }
1436 :
1437 : // If the original symbol is present we expect the Asan version to be
1438 : // present as well. The converse it not necessarily true, as the symbol
1439 : // can be reused in place by the transform in some cases. We also expect
1440 : // them to have no references (having been redirected to the Asan
1441 : // equivalents).
1442 E : if (has_name) {
1443 E : EXPECT_TRUE(has_asan_name);
1444 E : EXPECT_EQ(0u, name_refs);
1445 : }
1446 E : if (has_imp_name) {
1447 i : EXPECT_TRUE(has_imp_asan_name);
1448 i : EXPECT_EQ(0u, imp_name_refs);
1449 : }
1450 :
1451 : // If the Asan versions of the symbols are present we expect them to
1452 : // have references.
1453 E : if (has_asan_name) {
1454 E : EXPECT_LT(0u, asan_name_refs);
1455 : }
1456 E : if (has_imp_asan_name) {
1457 i : EXPECT_LT(0u, imp_asan_name_refs);
1458 : }
1459 E : }
1460 E : }
1461 :
1462 E : TEST_F(AsanTransformTest, AsanHooksAreStubbed) {
1463 E : ASSERT_NO_FATAL_FAILURE(ApplyTransformToIntegrationTestDll());
1464 :
1465 : // Load the transformed module without resolving its dependencies.
1466 : base::NativeLibrary lib =
1467 E : ::LoadLibraryEx(relinked_path_.value().c_str(),
1468 : NULL,
1469 : DONT_RESOLVE_DLL_REFERENCES);
1470 E : ASSERT_TRUE(lib != NULL);
1471 : // Make sure it's unloaded on failure.
1472 E : base::ScopedNativeLibrary lib_keeper(lib);
1473 :
1474 E : PEImage image(lib);
1475 E : ASSERT_TRUE(image.VerifyMagic());
1476 :
1477 : // Iterate over the image import descriptors. We want to make sure the
1478 : // one for syzyasan_rtl.dll is bound.
1479 E : DWORD size = image.GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
1480 E : PIMAGE_IMPORT_DESCRIPTOR iid = image.GetFirstImportChunk();
1481 E : ASSERT_TRUE(iid != NULL);
1482 E : ASSERT_GE(size, sizeof(IMAGE_IMPORT_DESCRIPTOR));
1483 E : for (; iid->FirstThunk; ++iid) {
1484 E : std::string module_name(reinterpret_cast<LPCSTR>(
1485 : image.RVAToAddr(iid->Name)));
1486 E : if (module_name == AsanTransform::kSyzyAsanDll)
1487 E : ASSERT_NE(0u, iid->TimeDateStamp);
1488 E : }
1489 :
1490 : // As all the hooks may refer to only two kinds of stubs, we expect to have
1491 : // exactly two entries in the set.
1492 E : FunctionsIATAddressSet hooks_iat_set;
1493 E : ASSERT_TRUE(image.EnumAllImports(&GetAsanHooksIATEntries, &hooks_iat_set));
1494 E : ASSERT_EQ(hooks_iat_set.size(), 2U);
1495 :
1496 : // Ensures that all stubs are in the thunks section.
1497 E : FunctionsIATAddressSet::iterator hook = hooks_iat_set.begin();
1498 E : for (; hook != hooks_iat_set.end(); ++hook) {
1499 E : PVOID stub_address = *hook;
1500 : PIMAGE_SECTION_HEADER stub_sec =
1501 E : image.GetImageSectionFromAddr(stub_address);
1502 E : ASSERT_STREQ(common::kThunkSectionName,
1503 E : reinterpret_cast<const char*>(stub_sec->Name));
1504 E : }
1505 E : }
1506 :
1507 E : TEST_F(AsanTransformTest, PeInterceptFunctions) {
1508 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
1509 :
1510 : BlockGraph::Block* b1 =
1511 E : block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 0x20, "testAsan_b1");
1512 : BlockGraph::Block* b2 =
1513 E : block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 0x20, "testAsan_b2");
1514 : BlockGraph::Block* b3 =
1515 E : block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 0x20, "testAsan_b3");
1516 E : ASSERT_TRUE(b1 != NULL);
1517 E : ASSERT_TRUE(b2 != NULL);
1518 E : ASSERT_TRUE(b3 != NULL);
1519 :
1520 E : ASSERT_TRUE(b1->references().empty());
1521 E : ASSERT_TRUE(b1->referrers().empty());
1522 E : ASSERT_TRUE(b2->references().empty());
1523 E : ASSERT_TRUE(b2->referrers().empty());
1524 E : ASSERT_TRUE(b3->references().empty());
1525 E : ASSERT_TRUE(b3->referrers().empty());
1526 :
1527 : // Add a reference from b2 to b1 and from b3 to b1.
1528 E : BlockGraph::Reference ref_b2_b1(BlockGraph::PC_RELATIVE_REF, 1, b1, 0, 0);
1529 E : BlockGraph::Reference ref_b3_b1(BlockGraph::PC_RELATIVE_REF, 1, b1, 1, 1);
1530 E : ASSERT_TRUE(b2->SetReference(0, ref_b2_b1));
1531 E : ASSERT_TRUE(b3->SetReference(1, ref_b3_b1));
1532 :
1533 E : EXPECT_EQ(2U, b1->referrers().size());
1534 :
1535 E : size_t num_blocks_pre_transform = block_graph_.blocks().size();
1536 E : size_t num_sections_pre_transform = block_graph_.sections().size();
1537 :
1538 : // Get the block hash.
1539 E : block_graph::BlockHash b1_hash(b1);
1540 E : std::string b1_hash_str = base::MD5DigestToBase16(b1_hash.md5_digest);
1541 E : MD5Hash b1_hashes[2] = {};
1542 E : strncpy(b1_hashes[0].hash, b1_hash_str.c_str(), sizeof(b1_hashes[0].hash));
1543 :
1544 : AsanIntercept b1_intercepts[] = {
1545 E : { "testAsan_b1", "_testAsan_b1", "foo.dll", b1_hashes, true },
1546 E : { NULL },
1547 : };
1548 :
1549 : // Find statically linked functions.
1550 E : asan_transform_.use_interceptors_ = true;
1551 E : asan_transform_.PeFindStaticallyLinkedFunctionsToIntercept(b1_intercepts,
1552 : &block_graph_);
1553 :
1554 E : ASSERT_EQ(1U, asan_transform_.static_intercepted_blocks_.size());
1555 E : EXPECT_EQ(b1, *(asan_transform_.static_intercepted_blocks_.begin()));
1556 :
1557 : // Intercept all calls to b1.
1558 E : EXPECT_TRUE(asan_transform_.PeInterceptFunctions(b1_intercepts,
1559 : policy_,
1560 : &block_graph_,
1561 E : header_block_));
1562 :
1563 : // The block graph should have grown by 3 blocks:
1564 : // - the Import Address Table (IAT),
1565 : // - the Import Name Table (INT),
1566 : // - the thunk.
1567 E : EXPECT_EQ(num_blocks_pre_transform + 3, block_graph_.blocks().size());
1568 :
1569 : // The .thunks section should have been added.
1570 E : EXPECT_EQ(num_sections_pre_transform + 1, block_graph_.sections().size());
1571 :
1572 E : BlockGraph::Section* thunk_section = block_graph_.FindSection(
1573 : common::kThunkSectionName);
1574 E : ASSERT_TRUE(thunk_section != NULL);
1575 :
1576 E : const BlockGraph::Block* block_in_thunk_section = NULL;
1577 : BlockGraph::BlockMap::const_iterator iter_blocks =
1578 E : block_graph_.blocks().begin();
1579 E : for (; iter_blocks != block_graph_.blocks().end(); ++iter_blocks) {
1580 E : if (iter_blocks->second.section() == thunk_section->id()) {
1581 : // There should be only one block in the thunk section.
1582 E : EXPECT_TRUE(block_in_thunk_section == NULL);
1583 E : block_in_thunk_section = &iter_blocks->second;
1584 : }
1585 E : }
1586 :
1587 : // Only the entry in the IAT should refer to b1.
1588 E : EXPECT_EQ(1U, b1->referrers().size());
1589 E : }
1590 :
1591 E : TEST_F(AsanTransformTest, CoffInterceptFunctions) {
1592 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDllObj());
1593 :
1594 E : size_t num_blocks_pre_transform = block_graph_.blocks().size();
1595 E : size_t num_sections_pre_transform = block_graph_.sections().size();
1596 :
1597 : AsanIntercept intercepts[] = {
1598 E : { "function2", "?function2@@YAHXZ", "", NULL, true },
1599 E : { NULL },
1600 : };
1601 :
1602 : // Intercept all calls to b1.
1603 E : asan_transform_.use_interceptors_ = true;
1604 E : EXPECT_TRUE(asan_transform_.CoffInterceptFunctions(intercepts,
1605 : policy_,
1606 : &block_graph_,
1607 E : header_block_));
1608 :
1609 : // The block graph should not have grown at all, as no thunks are necessary
1610 : // in the COFF instrumentation mode.
1611 E : EXPECT_EQ(num_blocks_pre_transform, block_graph_.blocks().size());
1612 :
1613 : // No sections should have been added.
1614 E : EXPECT_EQ(num_sections_pre_transform, block_graph_.sections().size());
1615 E : }
1616 :
1617 : namespace {
1618 :
1619 E : void GetImageSizeSubsampledInstrumentation(double rate, size_t* size) {
1620 E : ASSERT_LE(0.0, rate);
1621 E : ASSERT_GE(1.0, rate);
1622 E : ASSERT_TRUE(size != NULL);
1623 :
1624 E : base::FilePath test_dll_path = ::testing::GetOutputRelativePath(
1625 : testing::kTestDllName);
1626 :
1627 E : pe::PEFile pe_file;
1628 E : ASSERT_TRUE(pe_file.Init(test_dll_path));
1629 :
1630 E : BlockGraph block_graph;
1631 E : pe::ImageLayout layout(&block_graph);
1632 E : pe::Decomposer decomposer(pe_file);
1633 E : ASSERT_TRUE(decomposer.Decompose(&layout));
1634 :
1635 E : BlockGraph::Block* header_block = layout.blocks.GetBlockByAddress(
1636 : core::RelativeAddress(0));
1637 E : ASSERT_TRUE(header_block != NULL);
1638 :
1639 E : AsanTransform tx;
1640 E : tx.set_instrumentation_rate(rate);
1641 :
1642 E : pe::PETransformPolicy policy;
1643 E : ASSERT_TRUE(tx.TransformBlockGraph(&policy, &block_graph, header_block));
1644 :
1645 E : *size = 0;
1646 E : BlockGraph::BlockMap::const_iterator block_it = block_graph.blocks().begin();
1647 E : for (; block_it != block_graph.blocks().end(); ++block_it) {
1648 E : *size += block_it->second.size();
1649 E : }
1650 E : }
1651 :
1652 : } // namespace
1653 :
1654 E : TEST_F(AsanTransformTest, SubsampledInstrumentationTestDll) {
1655 E : size_t rate0 = 0;
1656 E : ASSERT_NO_FATAL_FAILURE(GetImageSizeSubsampledInstrumentation(0.0, &rate0));
1657 :
1658 E : size_t rate50 = 0;
1659 E : ASSERT_NO_FATAL_FAILURE(GetImageSizeSubsampledInstrumentation(0.5, &rate50));
1660 :
1661 E : size_t rate100 = 0;
1662 E : ASSERT_NO_FATAL_FAILURE(GetImageSizeSubsampledInstrumentation(1.0, &rate100));
1663 :
1664 E : size_t size100 = rate100 - rate0;
1665 E : size_t size50 = rate50 - rate0;
1666 :
1667 : // This could theoretically fail, but that would imply an extremely bad
1668 : // implementation of the underlying random number generator. There are about
1669 : // 1850 instructions being instrumented. Since this is effectively a fair
1670 : // coin toss we expect a standard deviation of 0.5 * sqrt(1850) = 22
1671 : // instructions. A 10% margin is 185 / 22 = 8.4 standard deviations. For
1672 : // |z| > 8.4, the p-value is 4.5e-17, or 17 nines of confidence. That should
1673 : // keep any flake largely at bay. Thus, if this fails it's pretty much certain
1674 : // the implementation is at fault.
1675 E : EXPECT_LE(40 * size100 / 100, size50);
1676 E : EXPECT_GE(60 * size100 / 100, size50);
1677 E : }
1678 :
1679 E : TEST_F(AsanTransformTest, PeInjectAsanParametersNoStackIds) {
1680 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
1681 :
1682 E : common::InflatedAsanParameters params;
1683 E : asan_transform_.set_asan_parameters(¶ms);
1684 E : EXPECT_TRUE(asan_transform_.PeInjectAsanParameters(
1685 E : policy_, &block_graph_, header_block_));
1686 :
1687 : // There should be a block containing parameters with the appropriate size.
1688 E : ASSERT_TRUE(asan_transform_.asan_parameters_block_ != NULL);
1689 E : EXPECT_EQ(sizeof(common::AsanParameters),
1690 E : asan_transform_.asan_parameters_block_->size());
1691 :
1692 : // The block should contain no references.
1693 E : EXPECT_TRUE(asan_transform_.asan_parameters_block_->references().empty());
1694 :
1695 : // The block should not be referred to at all.
1696 E : EXPECT_TRUE(asan_transform_.asan_parameters_block_->referrers().empty());
1697 E : }
1698 :
1699 E : TEST_F(AsanTransformTest, PeInjectAsanParametersStackIds) {
1700 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
1701 :
1702 E : common::InflatedAsanParameters params;
1703 E : params.ignored_stack_ids_set.insert(0xDEADBEEF);
1704 :
1705 E : asan_transform_.set_asan_parameters(¶ms);
1706 E : EXPECT_TRUE(asan_transform_.PeInjectAsanParameters(
1707 E : policy_, &block_graph_, header_block_));
1708 :
1709 : // There should be a block containing parameters with the appropriate size.
1710 E : ASSERT_TRUE(asan_transform_.asan_parameters_block_ != NULL);
1711 E : EXPECT_EQ(sizeof(common::AsanParameters) + 2 * sizeof(common::AsanStackId),
1712 E : asan_transform_.asan_parameters_block_->size());
1713 :
1714 : // The block should contain one reference to itself, from and to the
1715 : // appropriate place.
1716 E : EXPECT_EQ(1u, asan_transform_.asan_parameters_block_->references().size());
1717 E : BlockGraph::Reference ignored_stack_ids_ref(
1718 : BlockGraph::ABSOLUTE_REF,
1719 : BlockGraph::Reference::kMaximumSize,
1720 : asan_transform_.asan_parameters_block_,
1721 : sizeof(common::AsanParameters),
1722 : sizeof(common::AsanParameters));
1723 : BlockGraph::Block::ReferenceMap::const_iterator ignored_stack_ids_ref_it =
1724 E : asan_transform_.asan_parameters_block_->references().begin();
1725 E : EXPECT_EQ(offsetof(common::AsanParameters, ignored_stack_ids),
1726 E : ignored_stack_ids_ref_it->first);
1727 E : EXPECT_EQ(ignored_stack_ids_ref, ignored_stack_ids_ref_it->second);
1728 :
1729 : // The block should only be referred to by itself.
1730 E : EXPECT_EQ(1u, asan_transform_.asan_parameters_block_->referrers().size());
1731 E : }
1732 :
1733 E : TEST_F(AsanTransformTest, FindHeapInitAndCrtHeapBlocks) {
1734 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
1735 :
1736 E : asan_transform_.FindHeapInitAndCrtHeapBlocks(&block_graph_);
1737 :
1738 E : ASSERT_FALSE(asan_transform_.heap_init_blocks_.empty());
1739 E : for (const auto& iter : asan_transform_.heap_init_blocks_)
1740 E : EXPECT_NE(std::string::npos, iter->name().find("_acrt_initialize_heap"));
1741 E : }
1742 :
1743 E : TEST_F(AsanTransformTest, ShouldSkipBlock) {
1744 E : testing::DummyTransformPolicy policy;
1745 :
1746 : BlockGraph::Block* b1 =
1747 E : block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 0x20, "testAsan_b1");
1748 : BlockGraph::Block* b2 =
1749 E : block_graph_.AddBlock(BlockGraph::DATA_BLOCK, 0x20, "testAsan_b2");
1750 E : ASSERT_TRUE(b1 != NULL);
1751 E : ASSERT_TRUE(b2 != NULL);
1752 :
1753 : // A code block should not be skipped according to the dummy policy.
1754 E : EXPECT_FALSE(asan_transform_.ShouldSkipBlock(&policy, b1));
1755 :
1756 : // _heap_init block should be skipped.
1757 E : asan_transform_.heap_init_blocks_.push_back(b1);
1758 E : EXPECT_TRUE(asan_transform_.ShouldSkipBlock(&policy, b1));
1759 E : asan_transform_.heap_init_blocks_.clear();
1760 E : EXPECT_FALSE(asan_transform_.ShouldSkipBlock(&policy, b1));
1761 :
1762 : // A block in static_intercepted_blocks_ should be skipped.
1763 E : asan_transform_.static_intercepted_blocks_.insert(b1);
1764 E : EXPECT_TRUE(asan_transform_.ShouldSkipBlock(&policy, b1));
1765 E : asan_transform_.static_intercepted_blocks_.erase(b1);
1766 E : EXPECT_FALSE(asan_transform_.ShouldSkipBlock(&policy, b1));
1767 :
1768 : // A data block should be skipped according to the dummy policy.
1769 E : EXPECT_TRUE(asan_transform_.ShouldSkipBlock(&policy, b2));
1770 E : }
1771 :
1772 E : TEST_F(AsanTransformTest, PatchCRTHeapInitialization) {
1773 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
1774 :
1775 : // Keep a reference to all the heap initialization blocks that should be
1776 : // patched.
1777 :
1778 : std::map<const BlockGraph::Block*,
1779 : std::pair<BlockGraph::Offset, BlockGraph::Reference>>
1780 E : heap_init_blocks;
1781 :
1782 E : pe::transforms::ImportedModule kernel32("kernel32.dll");
1783 E : pe::transforms::PEAddImportsTransform kernel32_transform;
1784 :
1785 E : size_t get_process_heap_idx = kernel32.AddSymbol(
1786 : "GetProcessHeap", pe::transforms::ImportedModule::kFindOnly);
1787 E : kernel32_transform.AddModule(&kernel32);
1788 :
1789 E : EXPECT_TRUE(block_graph::ApplyBlockGraphTransform(&kernel32_transform,
1790 : policy_,
1791 : &block_graph_,
1792 E : header_block_));
1793 E : BlockGraph::Reference get_process_heap_ref;
1794 E : EXPECT_TRUE(kernel32.GetSymbolReference(get_process_heap_idx,
1795 E : &get_process_heap_ref));
1796 :
1797 E : for (const auto& iter : block_graph_.blocks()) {
1798 E : if (iter.second.name().find("_acrt_initialize_heap") == std::string::npos)
1799 E : continue;
1800 E : for (const auto& ref : iter.second.references()) {
1801 E : if (ref.second == get_process_heap_ref)
1802 E : heap_init_blocks[&iter.second] = ref;
1803 E : }
1804 E : }
1805 E : EXPECT_FALSE(heap_init_blocks.empty());
1806 :
1807 : // Apply the Asan transform.
1808 E : asan_transform_.use_interceptors_ = true;
1809 E : asan_transform_.use_liveness_analysis_ = true;
1810 E : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
1811 E : &asan_transform_, &pe_policy_, &block_graph_, header_block_));
1812 :
1813 : // Get a referenece to the heap create function that has been used to patch
1814 : // these blocks.
1815 E : pe::transforms::ImportedModule asan_module(
1816 : AsanTransform::kSyzyAsanDll);
1817 E : pe::transforms::PEAddImportsTransform asan_import_transform;
1818 E : size_t asan_heap_create_idx = asan_module.AddSymbol(
1819 : "asan_HeapCreate", pe::transforms::ImportedModule::kFindOnly);
1820 E : asan_import_transform.AddModule(&asan_module);
1821 E : EXPECT_TRUE(block_graph::ApplyBlockGraphTransform(&asan_import_transform,
1822 : policy_,
1823 : &block_graph_,
1824 E : header_block_));
1825 E : BlockGraph::Reference asan_heap_create_ref;
1826 E : EXPECT_TRUE(asan_module.GetSymbolReference(asan_heap_create_idx,
1827 E : &asan_heap_create_ref));
1828 :
1829 : // Verify that the patched blocks contains a reference to this heap create
1830 : // function.
1831 E : for (const auto& iter : heap_init_blocks) {
1832 E : const BlockGraph::Block* block = iter.first;
1833 E : BlockGraph::Offset offset = iter.second.first;
1834 :
1835 : // The blocks should contain a reference to a data block in the Syzygy thunk
1836 : // section.
1837 E : BlockGraph::Reference thunk_data_ref;
1838 E : EXPECT_TRUE(block->GetReference(offset, &thunk_data_ref));
1839 : BlockGraph::SectionId syzygy_section =
1840 E : block_graph_.FindSection(common::kThunkSectionName)->id();
1841 E : EXPECT_EQ(syzygy_section, thunk_data_ref.referenced()->section());
1842 E : EXPECT_EQ(BlockGraph::DATA_BLOCK, thunk_data_ref.referenced()->type());
1843 E : EXPECT_EQ(1, thunk_data_ref.referenced()->references().size());
1844 :
1845 : // This data block should refer to a code block also in the Syzygy thunk
1846 : // section.
1847 E : BlockGraph::Reference thunk_code_ref;
1848 E : EXPECT_TRUE(thunk_data_ref.referenced()->GetReference(0,
1849 : &thunk_code_ref));
1850 E : EXPECT_EQ(syzygy_section, thunk_code_ref.referenced()->section());
1851 E : EXPECT_EQ(BlockGraph::CODE_BLOCK, thunk_code_ref.referenced()->type());
1852 E : EXPECT_EQ(1, thunk_code_ref.referenced()->references().size());
1853 :
1854 : // And lastly, this code block should refer to the HeapCreate function.
1855 E : BlockGraph::Reference thunk_heap_create_ref;
1856 : BlockGraph::Offset ref_idx =
1857 E : thunk_code_ref.referenced()->references().begin()->first;
1858 E : EXPECT_TRUE(thunk_code_ref.referenced()->GetReference(ref_idx,
1859 : &thunk_heap_create_ref));
1860 E : EXPECT_EQ(asan_heap_create_ref, thunk_heap_create_ref);
1861 E : }
1862 E : }
1863 :
1864 E : TEST_F(AsanTransformTest, HotPatchingSection) {
1865 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
1866 :
1867 E : asan_transform_.set_hot_patching(true);
1868 E : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
1869 E : &asan_transform_, policy_, &block_graph_, header_block_));
1870 :
1871 : // Look for the presence of the hot patching section.
1872 E : const BlockGraph::Section* hp_section = nullptr;
1873 E : for (const auto& entry : block_graph_.sections()) {
1874 E : const BlockGraph::Section* sect = &entry.second;
1875 E : if (sect->name() == common::kHotPatchingMetadataSectionName) {
1876 E : hp_section = sect;
1877 : }
1878 E : }
1879 E : ASSERT_NE(nullptr, hp_section);
1880 :
1881 : // We iterate over the blocks and check that a block is in the list of hot
1882 : // patched blocks iff it has been prepared for hot patching.
1883 E : std::unordered_set<const BlockGraph::Block*> hot_patched_set_(
1884 : asan_transform_.hot_patched_blocks_.begin(),
1885 : asan_transform_.hot_patched_blocks_.end());
1886 E : for (const auto& entry : block_graph_.blocks()) {
1887 E : const BlockGraph::Block* block = &entry.second;
1888 :
1889 : // We check |padding_before| to see if the block has been prepared for hot
1890 : // patching.
1891 E : size_t expected_padding = hot_patched_set_.count(block) ? 5U : 0U;
1892 E : EXPECT_EQ(expected_padding, block->padding_before());
1893 E : }
1894 E : }
1895 :
1896 : } // namespace transforms
1897 : } // namespace instrument
|