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/string_util.h"
24 : #include "base/stringprintf.h"
25 : #include "base/files/scoped_temp_dir.h"
26 : #include "base/win/pe_image.h"
27 : #include "gtest/gtest.h"
28 : #include "syzygy/block_graph/basic_block_assembler.h"
29 : #include "syzygy/common/defs.h"
30 : #include "syzygy/core/unittest_util.h"
31 : #include "syzygy/instrument/transforms/unittest_util.h"
32 : #include "syzygy/pe/decomposer.h"
33 : #include "syzygy/pe/pe_file.h"
34 : #include "syzygy/pe/pe_relinker.h"
35 : #include "syzygy/pe/pe_utils.h"
36 : #include "syzygy/pe/unittest_util.h"
37 : #include "third_party/distorm/files/include/mnemonics.h"
38 :
39 : namespace instrument {
40 : namespace transforms {
41 :
42 : namespace {
43 :
44 : using block_graph::BasicBlock;
45 : using block_graph::BasicCodeBlock;
46 : using block_graph::BasicBlockSubGraph;
47 : using block_graph::BlockGraph;
48 : using block_graph::Instruction;
49 : using block_graph::RelativeAddressFilter;
50 : using core::RelativeAddress;
51 : typedef AsanBasicBlockTransform::MemoryAccessMode AsanMemoryAccessMode;
52 : typedef AsanBasicBlockTransform::AsanHookMap HookMap;
53 : typedef AsanBasicBlockTransform::AsanHookMapEntryKey HookMapEntryKey;
54 :
55 : // A derived class to expose protected members for unit-testing.
56 : class TestAsanBasicBlockTransform : public AsanBasicBlockTransform {
57 : public:
58 : using AsanBasicBlockTransform::InstrumentBasicBlock;
59 :
60 E : explicit TestAsanBasicBlockTransform(AsanHookMap* hooks_check_access)
61 : : AsanBasicBlockTransform(hooks_check_access) {
62 E : }
63 : };
64 :
65 : class AsanTransformTest : public testing::TestDllTransformTest {
66 : public:
67 : AsanTransformTest() :
68 : basic_block_("test block"),
69 : bb_asm_(basic_block_.instructions().begin(),
70 E : &basic_block_.instructions()) {
71 E : }
72 :
73 : void AddHookRef(const std::string& hook_name,
74 : AsanBasicBlockTransform::MemoryAccessMode access_kind,
75 : int access_size,
76 : uint16_t opcode,
77 E : bool save_flags) {
78 : HookMapEntryKey map_key = {
79 E : access_kind,
80 E : access_size,
81 E : opcode,
82 : save_flags
83 E : };
84 : hooks_check_access_[map_key] =
85 E : block_graph_.AddBlock(BlockGraph::CODE_BLOCK, 4, hook_name);
86 : // Set up the references to the hooks needed by SyzyAsan.
87 : hooks_check_access_ref_[map_key] =
88 : BlockGraph::Reference(BlockGraph::ABSOLUTE_REF, 4,
89 E : hooks_check_access_[map_key], 0, 0);
90 E : }
91 :
92 E : void InitHooksRefs() {
93 : // Initialize the read access hooks.
94 E : for (int access_size = 1; access_size <= 8; access_size *= 2) {
95 : std::string name =
96 E : base::StringPrintf("asan_check_%d_byte_read_access", access_size);
97 : AddHookRef(name, AsanBasicBlockTransform::kReadAccess, access_size, 0,
98 E : true);
99 E : name += "_no_flags";
100 : AddHookRef(name, AsanBasicBlockTransform::kReadAccess, access_size, 0,
101 E : false);
102 E : }
103 : // Initialize the write access hooks.
104 E : for (int access_size = 1; access_size <= 8; access_size *= 2) {
105 : std::string name =
106 E : base::StringPrintf("asan_check_%d_byte_write_access", access_size);
107 : AddHookRef(name, AsanBasicBlockTransform::kWriteAccess, access_size, 0,
108 E : true);
109 E : name += "_no_flags";
110 : AddHookRef(name, AsanBasicBlockTransform::kWriteAccess, access_size, 0,
111 E : false);
112 E : }
113 :
114 E : const _InstructionType strings[] = { I_CMPS, I_MOVS, I_STOS };
115 E : int strings_length = arraysize(strings);
116 :
117 E : for (int access_size = 1; access_size <= 4; access_size *= 2) {
118 E : for (int inst = 0; inst < strings_length; ++inst) {
119 E : uint16_t opcode = strings[inst];
120 : const char* opcode_str =
121 E : reinterpret_cast<const char*>(GET_MNEMONIC_NAME(opcode));
122 : std::string name =
123 : base::StringPrintf("asan_check_repz_%d_byte_%s_access",
124 E : access_size, opcode_str);
125 E : StringToLowerASCII(&name);
126 : AddHookRef(name, AsanBasicBlockTransform::kRepzAccess, access_size,
127 E : opcode, true);
128 E : }
129 E : }
130 :
131 : // Initialize special instruction hooks.
132 E : for (int access_size = 1; access_size <= 4; access_size *= 2) {
133 E : for (int inst = 0; inst < strings_length; ++inst) {
134 E : uint16_t opcode = strings[inst];
135 : const char* opcode_str =
136 E : reinterpret_cast<const char*>(GET_MNEMONIC_NAME(opcode));
137 :
138 : // Initialize the strings without prefix access hooks.
139 : std::string name =
140 : base::StringPrintf("asan_check_%d_byte_%s_access",
141 E : access_size, opcode_str);
142 E : StringToLowerASCII(&name);
143 : AddHookRef(name, AsanBasicBlockTransform::kInstrAccess, access_size,
144 E : opcode, true);
145 :
146 : // Initialize the strings with prefix access hooks.
147 : std::string repz_name =
148 : base::StringPrintf("asan_check_repz_%d_byte_%s_access",
149 E : access_size, opcode_str);
150 E : StringToLowerASCII(&repz_name);
151 : AddHookRef(repz_name, AsanBasicBlockTransform::kRepzAccess, access_size,
152 E : opcode, true);
153 E : }
154 E : }
155 E : }
156 :
157 E : bool AddInstructionFromBuffer(const uint8* data, size_t length) {
158 E : DCHECK(data != NULL);
159 E : DCHECK(length < core::AssemblerImpl::kMaxInstructionLength);
160 :
161 E : block_graph::Instruction temp;
162 E : if (!block_graph::Instruction::FromBuffer(data, length, &temp))
163 i : return false;
164 :
165 : // Append this instruction to the basic block.
166 E : basic_block_.instructions().push_back(temp);
167 :
168 E : return true;
169 E : }
170 :
171 : // Some handy constants we'll use throughout the tests.
172 : // @{
173 : static const BasicBlock::Size kDataSize;
174 : static const uint8 kBlockData[];
175 : // @}
176 :
177 : protected:
178 : base::ScopedTempDir temp_dir_;
179 : AsanTransform asan_transform_;
180 : HookMap hooks_check_access_ref_;
181 : std::map<HookMapEntryKey, BlockGraph::Block*> hooks_check_access_;
182 : BasicCodeBlock basic_block_;
183 : block_graph::BasicBlockAssembler bb_asm_;
184 : };
185 :
186 : const BasicBlock::Size AsanTransformTest::kDataSize = 32;
187 : const uint8 AsanTransformTest::kBlockData[AsanTransformTest::kDataSize] = {};
188 :
189 : } // namespace
190 :
191 E : TEST_F(AsanTransformTest, SetInstrumentDLLName) {
192 E : asan_transform_.set_instrument_dll_name("foo");
193 E : ASSERT_EQ(strcmp(asan_transform_.instrument_dll_name(), "foo"), 0);
194 E : }
195 :
196 E : TEST_F(AsanTransformTest, SetUseLivenessFlag) {
197 E : EXPECT_FALSE(asan_transform_.use_liveness_analysis());
198 E : asan_transform_.set_use_liveness_analysis(true);
199 E : EXPECT_TRUE(asan_transform_.use_liveness_analysis());
200 E : asan_transform_.set_use_liveness_analysis(false);
201 E : EXPECT_FALSE(asan_transform_.use_liveness_analysis());
202 :
203 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
204 E : EXPECT_FALSE(bb_transform.use_liveness_analysis());
205 E : bb_transform.set_use_liveness_analysis(true);
206 E : EXPECT_TRUE(bb_transform.use_liveness_analysis());
207 E : bb_transform.set_use_liveness_analysis(false);
208 E : EXPECT_FALSE(bb_transform.use_liveness_analysis());
209 E : }
210 :
211 E : TEST_F(AsanTransformTest, SetRemoveRedundantChecksFlag) {
212 E : EXPECT_FALSE(asan_transform_.remove_redundant_checks());
213 E : asan_transform_.set_remove_redundant_checks(true);
214 E : EXPECT_TRUE(asan_transform_.remove_redundant_checks());
215 E : asan_transform_.set_remove_redundant_checks(false);
216 E : EXPECT_FALSE(asan_transform_.remove_redundant_checks());
217 :
218 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
219 E : EXPECT_FALSE(bb_transform.remove_redundant_checks());
220 E : bb_transform.set_remove_redundant_checks(true);
221 E : EXPECT_TRUE(bb_transform.remove_redundant_checks());
222 E : bb_transform.set_remove_redundant_checks(false);
223 E : EXPECT_FALSE(bb_transform.remove_redundant_checks());
224 E : }
225 :
226 E : TEST_F(AsanTransformTest, ApplyAsanTransform) {
227 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll());
228 :
229 : ASSERT_TRUE(block_graph::ApplyBlockGraphTransform(
230 E : &asan_transform_, &block_graph_, dos_header_block_));
231 :
232 : // TODO(sebmarchand): Ensure that each memory access is instrumented by
233 : // decomposing each block of the new block-graph into basic blocks and walk
234 : // through their instructions. For now it's not possible due to an issue with
235 : // the labels in the new block-graph.
236 E : }
237 :
238 E : TEST_F(AsanTransformTest, InjectAsanHooks) {
239 : // Add a read access to the memory.
240 E : bb_asm_.mov(core::eax, block_graph::Operand(core::ebx));
241 : // Add a write access to the memory.
242 E : bb_asm_.mov(block_graph::Operand(core::ecx), core::edx);
243 :
244 : // Add source ranges to the instruction.
245 E : block_graph::Instruction& i1 = *basic_block_.instructions().begin();
246 : Instruction::SourceRange source_range =
247 E : Instruction::SourceRange(RelativeAddress(1000), i1.size());
248 E : i1.set_source_range(source_range);
249 :
250 : // Instrument this basic block.
251 E : InitHooksRefs();
252 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
253 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
254 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
255 :
256 : // Ensure that the basic block is instrumented.
257 :
258 : // We had 2 instructions initially, and for each of them we add 3
259 : // instructions, so we expect to have 2 + 3 * 2 = 8 instructions.
260 E : ASSERT_EQ(basic_block_.instructions().size(), 8);
261 :
262 : // Walk through the instructions to ensure that the Asan hooks have been
263 : // injected.
264 : BasicBlock::Instructions::const_iterator iter_inst =
265 E : basic_block_.instructions().begin();
266 :
267 E : Instruction::SourceRange empty_source_range;
268 E : ASSERT_TRUE(empty_source_range != source_range);
269 :
270 : // First we check if the first memory access is instrumented as a 4 byte read
271 : // access. We also validate that the instrumentation has not had source range
272 : // information added.
273 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
274 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_PUSH);
275 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
276 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_LEA);
277 E : ASSERT_EQ(empty_source_range, iter_inst->source_range());
278 E : ASSERT_EQ(iter_inst->references().size(), 1);
279 : HookMapEntryKey check_4_byte_read_key =
280 E : { AsanBasicBlockTransform::kReadAccess, 4, 0, true };
281 : ASSERT_TRUE(iter_inst->references().begin()->second.block()
282 E : == hooks_check_access_[check_4_byte_read_key]);
283 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_CALL);
284 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_MOV);
285 :
286 : // Then we check if the second memory access is well instrumented as a 4 byte
287 : // write access.
288 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_PUSH);
289 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_LEA);
290 E : ASSERT_EQ(iter_inst->references().size(), 1);
291 : HookMapEntryKey check_4_byte_write_key =
292 E : { AsanBasicBlockTransform::kWriteAccess, 4, 0, true };
293 : ASSERT_TRUE(iter_inst->references().begin()->second.block()
294 E : == hooks_check_access_[check_4_byte_write_key]);
295 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_CALL);
296 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_MOV);
297 :
298 E : ASSERT_TRUE(iter_inst == basic_block_.instructions().end());
299 E : }
300 :
301 E : TEST_F(AsanTransformTest, InjectAsanHooksWithSourceRange) {
302 : // Add a read access to the memory.
303 E : bb_asm_.mov(core::eax, block_graph::Operand(core::ebx));
304 :
305 : // Add a source range to the instruction.
306 E : block_graph::Instruction& i1 = *basic_block_.instructions().begin();
307 : Instruction::SourceRange source_range =
308 E : Instruction::SourceRange(RelativeAddress(1000), i1.size());
309 E : i1.set_source_range(source_range);
310 :
311 : // Keep track of basic block size.
312 E : uint32 before_instructions_count = basic_block_.instructions().size();
313 :
314 : // Instrument this basic block.
315 E : InitHooksRefs();
316 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
317 E : bb_transform.set_debug_friendly(true);
318 :
319 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
320 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
321 :
322 : // Ensure this basic block is instrumented.
323 E : uint32 after_instructions_count = basic_block_.instructions().size();
324 E : ASSERT_LT(before_instructions_count, after_instructions_count);
325 :
326 : // Walk through the instructions and validate the source range.
327 : BasicBlock::Instructions::const_iterator iter_inst =
328 E : basic_block_.instructions().begin();
329 :
330 E : for ( ; iter_inst != basic_block_.instructions().end(); ++iter_inst)
331 E : EXPECT_EQ(source_range, iter_inst->source_range());
332 E : }
333 :
334 E : TEST_F(AsanTransformTest, InstrumentDifferentKindOfInstructions) {
335 E : uint32 instrumentable_instructions = 0;
336 :
337 : // Generate a bunch of instrumentable and non instrumentable instructions.
338 E : bb_asm_.mov(core::eax, block_graph::Operand(core::ebx));
339 E : instrumentable_instructions++;
340 E : bb_asm_.mov(block_graph::Operand(core::ecx), core::edx);
341 E : instrumentable_instructions++;
342 E : bb_asm_.call(block_graph::Operand(core::ecx));
343 E : instrumentable_instructions++;
344 E : bb_asm_.jmp(block_graph::Operand(core::ecx));
345 E : instrumentable_instructions++;
346 E : bb_asm_.push(block_graph::Operand(core::eax));
347 E : instrumentable_instructions++;
348 :
349 : // Non-instrumentable.
350 E : bb_asm_.lea(core::eax, block_graph::Operand(core::ecx));
351 :
352 : uint32 expected_instructions_count = basic_block_.instructions().size()
353 E : + 3 * instrumentable_instructions;
354 : // Instrument this basic block.
355 E : InitHooksRefs();
356 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
357 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
358 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
359 E : ASSERT_EQ(basic_block_.instructions().size(), expected_instructions_count);
360 E : }
361 :
362 E : TEST_F(AsanTransformTest, InstrumentAndRemoveRedundantChecks) {
363 E : uint32 instrumentable_instructions = 0;
364 :
365 : // Generate a bunch of instrumentable and non instrumentable instructions.
366 : // We generate operand [ecx] multiple time as a redundant memory access.
367 E : bb_asm_.mov(core::eax, block_graph::Operand(core::ecx));
368 E : instrumentable_instructions++;
369 E : bb_asm_.mov(block_graph::Operand(core::ecx), core::edx);
370 : // Validate that indirect call clear the memory state.
371 E : bb_asm_.call(block_graph::Operand(core::ecx));
372 E : bb_asm_.push(block_graph::Operand(core::eax));
373 E : instrumentable_instructions++;
374 E : bb_asm_.mov(core::eax, block_graph::Operand(core::ecx));
375 E : instrumentable_instructions++;
376 E : bb_asm_.jmp(block_graph::Operand(core::ecx));
377 :
378 : uint32 expected_instructions_count = basic_block_.instructions().size()
379 E : + 3 * instrumentable_instructions;
380 : // Instrument this basic block.
381 E : InitHooksRefs();
382 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
383 E : bb_transform.set_remove_redundant_checks(true);
384 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
385 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
386 E : ASSERT_EQ(basic_block_.instructions().size(), expected_instructions_count);
387 E : }
388 :
389 E : TEST_F(AsanTransformTest, NonInstrumentableStackBasedInstructions) {
390 : // DEC DWORD [EBP - 0x2830]
391 : static const uint8 kDec1[6] = { 0xff, 0x8d, 0xd0, 0xd7, 0xff, 0xff };
392 : // INC DWORD [EBP - 0x31c]
393 : static const uint8 kInc1[6] = { 0xff, 0x85, 0xe4, 0xfc, 0xff, 0xff };
394 : // INC DWORD [ESP + 0x1c]
395 : static const uint8 kInc2[4] = { 0xff, 0x44, 0x24, 0x1c };
396 : // NEG DWORD [EBP + 0x24]
397 : static const uint8 kNeg1[3] = { 0xf7, 0x5d, 0x24 };
398 : // FILD QWORD [EBP - 0x8]
399 : static const uint8 kFild1[3] = { 0xdf, 0x6d, 0xf8 };
400 : // FISTP QWORD [ESP + 0x28]
401 : static const uint8 kFistp1[4] = { 0xdf, 0x7c, 0x24, 0x28 };
402 : // MOV EDI, [EBP - 0x4]
403 : static const uint8 kMov1[3] = { 0x8b, 0x7d, 0xfc };
404 : // MOV EAX, [EBP - 0x104]
405 : static const uint8 kMov2[6] = { 0x8b, 0x85, 0xfc, 0xfe, 0xff, 0xff };
406 :
407 E : ASSERT_TRUE(AddInstructionFromBuffer(kDec1, sizeof(kDec1)));
408 E : ASSERT_TRUE(AddInstructionFromBuffer(kInc1, sizeof(kInc1)));
409 E : ASSERT_TRUE(AddInstructionFromBuffer(kInc2, sizeof(kInc2)));
410 E : ASSERT_TRUE(AddInstructionFromBuffer(kNeg1, sizeof(kNeg1)));
411 E : ASSERT_TRUE(AddInstructionFromBuffer(kFild1, sizeof(kFild1)));
412 E : ASSERT_TRUE(AddInstructionFromBuffer(kFistp1, sizeof(kFistp1)));
413 E : ASSERT_TRUE(AddInstructionFromBuffer(kMov1, sizeof(kMov1)));
414 E : ASSERT_TRUE(AddInstructionFromBuffer(kMov2, sizeof(kMov2)));
415 :
416 : // Keep track of the basic block size before Asan transform.
417 E : uint32 expected_basic_block_size = basic_block_.instructions().size();
418 :
419 : // Instrument this basic block.
420 E : InitHooksRefs();
421 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
422 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
423 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
424 :
425 : // Non-instrumentable instructions implies no change.
426 E : EXPECT_EQ(expected_basic_block_size, basic_block_.instructions().size());
427 E : }
428 :
429 E : TEST_F(AsanTransformTest, InstrumentableStackBasedUnsafeInstructions) {
430 : // DEC DWORD [EBP - 0x2830]
431 : static const uint8 kDec1[6] = { 0xff, 0x8d, 0xd0, 0xd7, 0xff, 0xff };
432 :
433 E : ASSERT_TRUE(AddInstructionFromBuffer(kDec1, sizeof(kDec1)));
434 :
435 : // Keep track of the basic block size before Asan transform.
436 E : uint32 previous_basic_block_size = basic_block_.instructions().size();
437 :
438 : // Instrument this basic block considering invalid stack manipulation.
439 E : InitHooksRefs();
440 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
441 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
442 E : &basic_block_, AsanBasicBlockTransform::kUnsafeStackAccess));
443 :
444 : // This instruction should have been instrumented, and we must observe
445 : // a increase in size.
446 E : EXPECT_LT(previous_basic_block_size, basic_block_.instructions().size());
447 E : }
448 :
449 E : TEST_F(AsanTransformTest, NonInstrumentableSegmentBasedInstructions) {
450 : // add eax, fs:[eax]
451 : static const uint8 kAdd1[3] = { 0x64, 0x03, 0x00 };
452 : // inc gs:[eax]
453 : static const uint8 kInc1[3] = { 0x65, 0xFE, 0x00 };
454 :
455 E : ASSERT_TRUE(AddInstructionFromBuffer(kAdd1, sizeof(kAdd1)));
456 E : ASSERT_TRUE(AddInstructionFromBuffer(kInc1, sizeof(kInc1)));
457 :
458 : // Keep track of the basic block size before Asan transform.
459 E : uint32 expected_basic_block_size = basic_block_.instructions().size();
460 :
461 : // Instrument this basic block.
462 E : InitHooksRefs();
463 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
464 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
465 : &basic_block_,
466 E : AsanBasicBlockTransform::kSafeStackAccess));
467 :
468 : // Non-instrumentable instructions implies no change.
469 E : EXPECT_EQ(expected_basic_block_size, basic_block_.instructions().size());
470 E : }
471 :
472 E : TEST_F(AsanTransformTest, FilteredInstructionsNotInstrumented) {
473 : // Add a read access to the memory.
474 E : bb_asm_.mov(core::eax, block_graph::Operand(core::ebx));
475 : // Add a write access to the memory.
476 E : bb_asm_.mov(block_graph::Operand(core::ecx), core::edx);
477 :
478 : // Add a source range to the first instruction.
479 E : block_graph::Instruction& i1 = *basic_block_.instructions().begin();
480 : i1.set_source_range(Instruction::SourceRange(
481 E : RelativeAddress(1000), i1.size()));
482 :
483 : // Create a filter that blocks out that source range.
484 : RelativeAddressFilter filter(
485 E : RelativeAddressFilter::Range(RelativeAddress(0), 2000));
486 E : filter.Mark(RelativeAddressFilter::Range(RelativeAddress(995), 50));
487 :
488 : // Pass the filter to the BB transform.
489 E : InitHooksRefs();
490 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
491 E : bb_transform.set_filter(&filter);
492 :
493 : // Instrument this basic block.
494 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
495 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
496 :
497 : // Ensure that the basic block is instrumented, but only the second
498 : // instruction.
499 :
500 : // We had 2 instructions initially. For the second one we add 3
501 : // instructions, so we expect to have 1 + (1 + 3) = 5 instructions.
502 E : ASSERT_EQ(basic_block_.instructions().size(), 5);
503 :
504 : // Walk through the instructions to ensure that the Asan hooks have been
505 : // injected.
506 : BasicBlock::Instructions::const_iterator iter_inst =
507 E : basic_block_.instructions().begin();
508 :
509 : // Ensure the first instruction is not instrumented at all.
510 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_MOV);
511 :
512 : // Then we check if the second memory access is well instrumented as a 4 byte
513 : // write access.
514 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_PUSH);
515 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_LEA);
516 E : ASSERT_EQ(iter_inst->references().size(), 1);
517 : HookMapEntryKey check_4_byte_write_key =
518 E : { AsanBasicBlockTransform::kWriteAccess, 4, 0, true };
519 : ASSERT_TRUE(iter_inst->references().begin()->second.block()
520 E : == hooks_check_access_[check_4_byte_write_key]);
521 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_CALL);
522 E : ASSERT_TRUE((iter_inst++)->representation().opcode == I_MOV);
523 :
524 E : ASSERT_TRUE(iter_inst == basic_block_.instructions().end());
525 E : }
526 :
527 E : TEST_F(AsanTransformTest, InstrumentableStringInstructions) {
528 : static const uint8 movsd[1] = { 0xA5 };
529 : static const uint8 movsw[2] = { 0x66, 0xA5 };
530 : static const uint8 movsb[1] = { 0xA4 };
531 :
532 : static const uint8 cmpsd[1] = { 0xA7 };
533 : static const uint8 cmpsw[2] = { 0x66, 0xA7 };
534 : static const uint8 cmpsb[1] = { 0xA6 };
535 :
536 : static const uint8 stosd[1] = { 0xAB };
537 : static const uint8 stosw[2] = { 0x66, 0xAB };
538 : static const uint8 stosb[1] = { 0xAA };
539 :
540 E : EXPECT_TRUE(AddInstructionFromBuffer(movsd, sizeof(movsd)));
541 E : EXPECT_TRUE(AddInstructionFromBuffer(movsw, sizeof(movsw)));
542 E : EXPECT_TRUE(AddInstructionFromBuffer(movsb, sizeof(movsb)));
543 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsd, sizeof(cmpsd)));
544 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsw, sizeof(cmpsw)));
545 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsb, sizeof(cmpsb)));
546 E : EXPECT_TRUE(AddInstructionFromBuffer(stosd, sizeof(stosd)));
547 E : EXPECT_TRUE(AddInstructionFromBuffer(stosw, sizeof(stosw)));
548 E : EXPECT_TRUE(AddInstructionFromBuffer(stosb, sizeof(stosb)));
549 :
550 : // Keep number of instrumentable instructions.
551 E : uint32 count_instructions = basic_block_.instructions().size();
552 :
553 : // Keep track of the basic block size before Asan transform.
554 E : uint32 basic_block_size = basic_block_.instructions().size();
555 :
556 : // Instrument this basic block.
557 E : InitHooksRefs();
558 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
559 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
560 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
561 :
562 : // Each instrumentable instructions implies 1 new instructions.
563 E : uint32 expected_basic_block_size = count_instructions + basic_block_size;
564 :
565 : // Validate basic block size.
566 E : ASSERT_EQ(basic_block_.instructions().size(), expected_basic_block_size);
567 E : }
568 :
569 E : TEST_F(AsanTransformTest, InstrumentableRepzStringInstructions) {
570 : static const uint8 movsd[2] = { 0xF3, 0xA5 };
571 : static const uint8 movsw[3] = { 0xF3, 0x66, 0xA5 };
572 : static const uint8 movsb[2] = { 0xF3, 0xA4 };
573 :
574 : static const uint8 cmpsd[2] = { 0xF3, 0xA7 };
575 : static const uint8 cmpsw[3] = { 0xF3, 0x66, 0xA7 };
576 : static const uint8 cmpsb[2] = { 0xF3, 0xA6 };
577 :
578 : static const uint8 stosd[2] = { 0xF3, 0xAB };
579 : static const uint8 stosw[3] = { 0xF3, 0x66, 0xAB };
580 : static const uint8 stosb[2] = { 0xF3, 0xAA };
581 :
582 E : EXPECT_TRUE(AddInstructionFromBuffer(movsd, sizeof(movsd)));
583 E : EXPECT_TRUE(AddInstructionFromBuffer(movsw, sizeof(movsw)));
584 E : EXPECT_TRUE(AddInstructionFromBuffer(movsb, sizeof(movsb)));
585 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsd, sizeof(cmpsd)));
586 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsw, sizeof(cmpsw)));
587 E : EXPECT_TRUE(AddInstructionFromBuffer(cmpsb, sizeof(cmpsb)));
588 E : EXPECT_TRUE(AddInstructionFromBuffer(stosd, sizeof(stosd)));
589 E : EXPECT_TRUE(AddInstructionFromBuffer(stosw, sizeof(stosw)));
590 E : EXPECT_TRUE(AddInstructionFromBuffer(stosb, sizeof(stosb)));
591 :
592 : // Keep number of instrumentable instructions.
593 E : uint32 count_instructions = basic_block_.instructions().size();
594 :
595 : // Keep track of the basic block size before Asan transform.
596 E : uint32 basic_block_size = basic_block_.instructions().size();
597 :
598 : // Instrument this basic block.
599 E : InitHooksRefs();
600 E : TestAsanBasicBlockTransform bb_transform(&hooks_check_access_ref_);
601 : ASSERT_TRUE(bb_transform.InstrumentBasicBlock(
602 E : &basic_block_, AsanBasicBlockTransform::kSafeStackAccess));
603 :
604 : // Each instrumentable instructions implies 1 new instructions.
605 E : uint32 expected_basic_block_size = count_instructions + basic_block_size;
606 :
607 : // Validate basic block size.
608 E : ASSERT_EQ(basic_block_.instructions().size(), expected_basic_block_size);
609 E : }
610 :
611 : namespace {
612 :
613 : using base::win::PEImage;
614 : typedef std::set<std::string> StringSet;
615 : typedef std::set<PVOID> FunctionsIATAddressSet;
616 : typedef std::vector<std::string> StringVector;
617 :
618 : const char kAsanRtlDll[] = "asan_rtl.dll";
619 :
620 : bool EnumKernel32HeapImports(const PEImage &image, LPCSTR module,
621 : DWORD ordinal, LPCSTR name, DWORD hint,
622 E : PIMAGE_THUNK_DATA iat, PVOID cookie) {
623 E : DCHECK(module != NULL);
624 E : DCHECK(cookie != NULL);
625 :
626 E : StringVector* modules = reinterpret_cast<StringVector*>(cookie);
627 :
628 E : if (_stricmp("kernel32.dll", module) == 0 && strncmp("Heap", name, 4) == 0) {
629 E : DCHECK(name != NULL);
630 E : modules->push_back(name);
631 : }
632 :
633 E : return true;
634 E : }
635 :
636 : bool EnumAsanImports(const PEImage &image, LPCSTR module,
637 : DWORD ordinal, LPCSTR name, DWORD hint,
638 E : PIMAGE_THUNK_DATA iat, PVOID cookie) {
639 E : DCHECK(module != NULL);
640 E : DCHECK(cookie != NULL);
641 :
642 E : StringSet* modules = reinterpret_cast<StringSet*>(cookie);
643 :
644 E : if (strcmp(kAsanRtlDll, module) == 0) {
645 E : DCHECK(name != NULL);
646 E : modules->insert(name);
647 : }
648 :
649 E : return true;
650 E : }
651 :
652 : bool GetAsanHooksIATEntries(const PEImage &image,
653 : LPCSTR module,
654 : DWORD ordinal,
655 : LPCSTR name,
656 : DWORD hint,
657 : PIMAGE_THUNK_DATA iat,
658 E : PVOID cookie) {
659 E : DCHECK(module != NULL);
660 E : DCHECK(cookie != NULL);
661 :
662 : FunctionsIATAddressSet* hooks_iat_entries =
663 E : reinterpret_cast<FunctionsIATAddressSet*>(cookie);
664 :
665 E : if (strcmp(kAsanRtlDll, module) != 0)
666 E : return true;
667 :
668 E : DCHECK(name != NULL);
669 :
670 : // Ensures that the function is an asan_check_access hook.
671 E : if (StartsWithASCII(name, "asan_check_", true /* case sensitive */))
672 E : hooks_iat_entries->insert(reinterpret_cast<PVOID>(iat->u1.Function));
673 :
674 E : return true;
675 E : }
676 :
677 : } // namespace
678 :
679 E : TEST_F(AsanTransformTest, ImportsAreRedirected) {
680 : base::FilePath asan_instrumented_dll = testing::GetExeTestDataRelativePath(
681 E : testing::kAsanInstrumentedTestDllName);
682 :
683 : // Load the transformed module without resolving its dependencies.
684 : base::NativeLibrary lib =
685 : ::LoadLibraryEx(asan_instrumented_dll.value().c_str(),
686 : NULL,
687 E : DONT_RESOLVE_DLL_REFERENCES);
688 E : ASSERT_TRUE(lib != NULL);
689 : // Make sure it's unloaded on failure.
690 E : base::ScopedNativeLibrary lib_keeper(lib);
691 :
692 E : PEImage image(lib);
693 E : ASSERT_TRUE(image.VerifyMagic());
694 E : StringSet imports;
695 E : ASSERT_TRUE(image.EnumAllImports(&EnumAsanImports, &imports));
696 :
697 E : StringVector heap_imports;
698 E : ASSERT_TRUE(image.EnumAllImports(&EnumKernel32HeapImports, &heap_imports));
699 :
700 : // This isn't strictly speaking a full test, as we only check that the new
701 : // imports have been added. It's however more trouble than it's worth to
702 : // test this fully for now.
703 E : StringSet expected;
704 E : for (size_t i = 0; i < heap_imports.size(); ++i) {
705 E : std::string asan_import = "asan_";
706 E : asan_import.append(heap_imports[i]);
707 E : expected.insert(asan_import);
708 E : }
709 E : expected.insert("asan_check_1_byte_read_access");
710 E : expected.insert("asan_check_2_byte_read_access");
711 E : expected.insert("asan_check_4_byte_read_access");
712 E : expected.insert("asan_check_8_byte_read_access");
713 E : expected.insert("asan_check_10_byte_read_access");
714 E : expected.insert("asan_check_16_byte_read_access");
715 E : expected.insert("asan_check_32_byte_read_access");
716 E : expected.insert("asan_check_1_byte_write_access");
717 E : expected.insert("asan_check_2_byte_write_access");
718 E : expected.insert("asan_check_4_byte_write_access");
719 E : expected.insert("asan_check_8_byte_write_access");
720 E : expected.insert("asan_check_10_byte_write_access");
721 E : expected.insert("asan_check_16_byte_write_access");
722 E : expected.insert("asan_check_32_byte_write_access");
723 :
724 E : expected.insert("asan_check_repz_4_byte_cmps_access");
725 E : expected.insert("asan_check_repz_4_byte_movs_access");
726 E : expected.insert("asan_check_repz_4_byte_stos_access");
727 E : expected.insert("asan_check_repz_2_byte_cmps_access");
728 E : expected.insert("asan_check_repz_2_byte_movs_access");
729 E : expected.insert("asan_check_repz_2_byte_stos_access");
730 E : expected.insert("asan_check_repz_1_byte_cmps_access");
731 E : expected.insert("asan_check_repz_1_byte_movs_access");
732 E : expected.insert("asan_check_repz_1_byte_stos_access");
733 :
734 E : expected.insert("asan_check_4_byte_cmps_access");
735 E : expected.insert("asan_check_4_byte_movs_access");
736 E : expected.insert("asan_check_4_byte_stos_access");
737 E : expected.insert("asan_check_2_byte_cmps_access");
738 E : expected.insert("asan_check_2_byte_movs_access");
739 E : expected.insert("asan_check_2_byte_stos_access");
740 E : expected.insert("asan_check_1_byte_cmps_access");
741 E : expected.insert("asan_check_1_byte_movs_access");
742 E : expected.insert("asan_check_1_byte_stos_access");
743 :
744 E : EXPECT_EQ(expected, imports);
745 E : }
746 :
747 E : TEST_F(AsanTransformTest, AsanHooksAreStubbed) {
748 : base::FilePath asan_instrumented_dll = testing::GetExeTestDataRelativePath(
749 E : testing::kAsanInstrumentedTestDllName);
750 :
751 : // Load the transformed module without resolving its dependencies.
752 : base::NativeLibrary lib =
753 : ::LoadLibraryEx(asan_instrumented_dll.value().c_str(),
754 : NULL,
755 E : DONT_RESOLVE_DLL_REFERENCES);
756 E : ASSERT_TRUE(lib != NULL);
757 : // Make sure it's unloaded on failure.
758 E : base::ScopedNativeLibrary lib_keeper(lib);
759 :
760 E : PEImage image(lib);
761 E : ASSERT_TRUE(image.VerifyMagic());
762 :
763 : // Iterate over the image import descriptors. We want to make sure the
764 : // one for asan_rtl.dll is bound.
765 E : DWORD size = image.GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
766 E : PIMAGE_IMPORT_DESCRIPTOR iid = image.GetFirstImportChunk();
767 E : ASSERT_TRUE(iid != NULL);
768 E : ASSERT_GE(size, sizeof(IMAGE_IMPORT_DESCRIPTOR));
769 E : for (; iid->FirstThunk; ++iid) {
770 : std::string module_name(reinterpret_cast<LPCSTR>(
771 E : image.RVAToAddr(iid->Name)));
772 E : if (module_name == kAsanRtlDll)
773 E : ASSERT_NE(0u, iid->TimeDateStamp);
774 E : }
775 :
776 : // As all the hooks may refer to only two kinds of stubs, we expect to have
777 : // exactly two entries in the set.
778 E : FunctionsIATAddressSet hooks_iat_set;
779 E : ASSERT_TRUE(image.EnumAllImports(&GetAsanHooksIATEntries, &hooks_iat_set));
780 E : ASSERT_EQ(hooks_iat_set.size(), 2U);
781 :
782 : // Ensures that all stubs are in the thunks section.
783 E : FunctionsIATAddressSet::iterator hook = hooks_iat_set.begin();
784 E : for (; hook != hooks_iat_set.end(); ++hook) {
785 E : PVOID stub_address = *hook;
786 : PIMAGE_SECTION_HEADER stub_sec =
787 E : image.GetImageSectionFromAddr(stub_address);
788 : ASSERT_STREQ(common::kThunkSectionName,
789 E : reinterpret_cast<const char*>(stub_sec->Name));
790 E : }
791 E : }
792 :
793 : } // namespace transforms
794 : } // namespace instrument
|