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