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