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

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

Coverage information generated Thu Jul 04 09:34:53 2013.