Coverage for /Syzygy/instrument/transforms/asan_transform_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
99.7%2872880.C++test

Line-by-line coverage:

   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

Coverage information generated Thu Mar 14 11:53:36 2013.