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