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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
89.7%3223590.C++source

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    :  #include "syzygy/instrument/transforms/asan_transform.h"
  16    :  
  17    :  #include <vector>
  18    :  
  19    :  #include "base/logging.h"
  20    :  #include "base/string_util.h"
  21    :  #include "base/stringprintf.h"
  22    :  #include "base/memory/ref_counted.h"
  23    :  #include "syzygy/block_graph/basic_block_assembler.h"
  24    :  #include "syzygy/block_graph/block_builder.h"
  25    :  #include "syzygy/common/defs.h"
  26    :  #include "syzygy/pe/block_util.h"
  27    :  #include "syzygy/pe/pe_utils.h"
  28    :  #include "syzygy/pe/transforms/add_imports_transform.h"
  29    :  #include "third_party/distorm/files/include/mnemonics.h"
  30    :  #include "third_party/distorm/files/src/x86defs.h"
  31    :  
  32    :  namespace instrument {
  33    :  namespace transforms {
  34    :  namespace {
  35    :  
  36    :  using block_graph::BasicBlock;
  37    :  using block_graph::BasicCodeBlock;
  38    :  using block_graph::BasicBlockAssembler;
  39    :  using block_graph::BasicBlockSubGraph;
  40    :  using block_graph::BasicBlockReference;
  41    :  using block_graph::BlockBuilder;
  42    :  using block_graph::BlockGraph;
  43    :  using block_graph::Displacement;
  44    :  using block_graph::Immediate;
  45    :  using block_graph::Instruction;
  46    :  using block_graph::Operand;
  47    :  using block_graph::TypedBlock;
  48    :  using block_graph::Value;
  49    :  using core::Register;
  50    :  using core::RegisterCode;
  51    :  using pe::transforms::AddImportsTransform;
  52    :  
  53    :  // A simple struct that can be used to let us access strings using TypedBlock.
  54    :  struct StringStruct {
  55    :    const char string[1];
  56    :  };
  57    :  
  58    :  typedef AddImportsTransform::ImportedModule ImportedModule;
  59    :  typedef AsanBasicBlockTransform::MemoryAccessMode AsanMemoryAccessMode;
  60    :  typedef AsanBasicBlockTransform::AsanHookMap HookMap;
  61    :  typedef std::vector<AsanBasicBlockTransform::AsanHookMapEntryKey>
  62    :      AccessHookParamVector;
  63    :  typedef TypedBlock<IMAGE_IMPORT_DESCRIPTOR> ImageImportDescriptor;
  64    :  typedef TypedBlock<StringStruct> String;
  65    :  
  66    :  // Returns true iff opcode should be instrumented.
  67  E :  bool ShouldInstrumentOpcode(uint16 opcode) {
  68  E :    switch (opcode) {
  69    :      case I_LEA:
  70    :      case I_CALL:
  71    :      case I_JMP:
  72  E :        return false;
  73    :      default:
  74  E :        return true;
  75    :    }
  76  E :  }
  77    :  
  78    :  // Computes the correct displacement, if any, for operand
  79    :  // number @p operand of @p instr.
  80    :  Displacement ComputeDisplacementForOperand(const Instruction& instr,
  81  E :                                             size_t operand) {
  82  E :    const _DInst& repr = instr.representation();
  83    :  
  84    :    DCHECK(repr.ops[operand].type == O_SMEM ||
  85  E :           repr.ops[operand].type == O_MEM);
  86    :  
  87  E :    size_t access_size_bytes = repr.ops[operand].size / 8;
  88  E :    if (repr.dispSize == 0)
  89  E :      return Displacement(access_size_bytes - 1);
  90    :  
  91  E :    BasicBlockReference reference;
  92  E :    if (instr.FindOperandReference(operand, &reference)) {
  93  E :      if (reference.referred_type() == BasicBlockReference::REFERRED_TYPE_BLOCK) {
  94    :        return Displacement(reference.block(),
  95  E :                            reference.offset() + access_size_bytes - 1);
  96  i :      } else {
  97  E :        return Displacement(reference.basic_block());
  98    :      }
  99  i :    } else {
 100  E :      return Displacement(repr.disp + access_size_bytes - 1);
 101    :    }
 102  E :  }
 103    :  
 104    :  // Returns true if operand @p op is instrumentable, e.g.
 105    :  // if it implies a memory access.
 106  E :  bool IsInstrumentable(const _Operand& op) {
 107  E :    switch (op.type) {
 108    :      case O_SMEM:
 109    :      case O_MEM:
 110  E :        return true;
 111    :  
 112    :      default:
 113  E :        return false;
 114    :    }
 115  E :  }
 116    :  
 117    :  // Returns true if opcode @p opcode is a special instruction.
 118    :  // Memory checks for special instructions (string instructions, instructions
 119    :  // with prefix, etc) are handled by calling specialized functions rather than
 120    :  // the standard memory checks.
 121  E :  bool IsSpecialInstruction(uint16_t opcode) {
 122  E :    switch (opcode) {
 123    :      case I_CMPS:
 124    :      case I_STOS:
 125    :      case I_MOVS:
 126  E :        return true;
 127    :  
 128    :      default:
 129  E :        return false;
 130    :    }
 131  E :  }
 132    :  
 133    :  // Decodes the first O_MEM or O_SMEM operand of @p instr, if any to the
 134    :  // corresponding Operand.
 135    :  bool DecodeMemoryAccess(const Instruction& instr,
 136    :      Operand* access,
 137  E :      AsanBasicBlockTransform::MemoryAccessInfo* info) {
 138  E :    DCHECK(access != NULL);
 139  E :    DCHECK(info != NULL);
 140  E :    const _DInst& repr = instr.representation();
 141    :  
 142    :    // Figure out which operand we're instrumenting.
 143  E :    size_t mem_op_id = -1;
 144  E :    if (IsInstrumentable(repr.ops[0]) && IsInstrumentable(repr.ops[1])) {
 145    :      // This happens with instructions like: MOVS [EDI], [ESI].
 146  E :      DCHECK(repr.ops[0].size == repr.ops[1].size);
 147  E :      mem_op_id = 0;
 148  E :    } else if (IsInstrumentable(repr.ops[0])) {
 149    :      // The first operand is instrumentable.
 150  E :      mem_op_id = 0;
 151  E :    } else if (IsInstrumentable(repr.ops[1])) {
 152    :      // The second operand is instrumentable.
 153  E :      mem_op_id = 1;
 154  E :    } else {
 155    :      // Neither of the first two operands is instrumentable.
 156  E :      return false;
 157    :    }
 158    :  
 159    :    // Determine the size of the access.
 160  E :    info->size = repr.ops[mem_op_id].size / 8;
 161    :  
 162    :    // Determine the kind of access (read/write/instr/repz).
 163  E :    if (FLAG_GET_PREFIX(repr.flags) & FLAG_REPNZ)
 164  i :      info->mode = AsanBasicBlockTransform::kRepnzAccess;
 165  E :    else if (FLAG_GET_PREFIX(repr.flags) & FLAG_REP)
 166  E :      info->mode = AsanBasicBlockTransform::kRepzAccess;
 167  E :    else if (IsSpecialInstruction(instr.opcode()))
 168  E :      info->mode = AsanBasicBlockTransform::kInstrAccess;
 169  E :    else if ((repr.flags & FLAG_DST_WR) && mem_op_id == 0) {
 170    :      // The first operand is written to.
 171  E :      info->mode = AsanBasicBlockTransform::kWriteAccess;
 172  E :    } else {
 173  E :      info->mode = AsanBasicBlockTransform::kReadAccess;
 174    :    }
 175    :  
 176    :    // Determine the opcode of this instruction (when needed)
 177    :    if (info->mode == AsanBasicBlockTransform::kRepnzAccess ||
 178    :        info->mode == AsanBasicBlockTransform::kRepzAccess ||
 179  E :        info->mode == AsanBasicBlockTransform::kInstrAccess) {
 180  E :      info->opcode = instr.opcode();
 181    :    }
 182    :  
 183    :    // Determine operand of the access.
 184  E :    if (repr.ops[mem_op_id].type == O_SMEM) {
 185    :      // Simple memory dereference with optional displacement.
 186  E :      Register base_reg(RegisterCode(repr.ops[mem_op_id].index - R_EAX));
 187    :      // Get the displacement for the operand.
 188  E :      Displacement displ = ComputeDisplacementForOperand(instr, mem_op_id);
 189    :  
 190  E :      *access = Operand(base_reg, displ);
 191  E :    } else if (repr.ops[0].type == O_MEM || repr.ops[1].type == O_MEM) {
 192    :      // Complex memory dereference.
 193  E :      Register index_reg(RegisterCode(repr.ops[mem_op_id].index - R_EAX));
 194  E :      core::ScaleFactor scale = core::kTimes1;
 195  E :      switch (repr.scale) {
 196    :        case 2:
 197  E :          scale = core::kTimes2;
 198  E :          break;
 199    :        case 4:
 200  E :          scale = core::kTimes4;
 201  E :          break;
 202    :        case 8:
 203  E :          scale = core::kTimes8;
 204    :          break;
 205    :        default:
 206    :          break;
 207    :      }
 208    :  
 209    :      // Get the displacement for the operand (if any).
 210  E :      Displacement displ = ComputeDisplacementForOperand(instr, mem_op_id);
 211    :  
 212    :      // Compute the full operand.
 213  E :      if (repr.base != R_NONE) {
 214  E :        Register base_reg(RegisterCode(repr.base - R_EAX));
 215  E :        if (displ.size() == core::kSizeNone) {
 216    :          // No displacement, it's a [base + index * scale] access.
 217  i :          *access = Operand(base_reg, index_reg, scale);
 218  i :        } else {
 219    :          // This is a [base + index * scale + displ] access.
 220  E :          *access = Operand(base_reg, index_reg, scale, displ);
 221    :        }
 222  E :      } else {
 223    :        // No base, this is an [index * scale + displ] access.
 224    :        // TODO(siggi): AFAIK, there's no encoding for [index * scale] without
 225    :        //    a displacement. If this assert fires, I'm proven wrong.
 226  E :        DCHECK_NE(core::kSizeNone, displ.size());
 227    :  
 228  E :        *access = Operand(index_reg, scale, displ);
 229    :      }
 230  E :    } else {
 231  i :      NOTREACHED();
 232    :  
 233  i :      return AsanBasicBlockTransform::kNoAccess;
 234    :    }
 235    :  
 236  E :    return true;
 237  E :  }
 238    :  
 239    :  // Use @p bb_asm to inject a hook to @p hook to instrument the access to the
 240    :  // address stored in the operand @p op.
 241    :  void InjectAsanHook(BasicBlockAssembler* bb_asm,
 242    :                      const AsanBasicBlockTransform::MemoryAccessInfo& info,
 243    :                      const Operand& op,
 244  E :                      BlockGraph::Reference* hook) {
 245  E :    DCHECK(hook != NULL);
 246    :  
 247    :    // Determine which kind of probe to inject.
 248    :    if (info.mode == AsanBasicBlockTransform::kReadAccess ||
 249  E :        info.mode == AsanBasicBlockTransform::kWriteAccess) {
 250    :      // The standard load/store probe assume the address is in EDX.
 251    :      // It restore the original version of EDX and cleanup the stack.
 252  E :      bb_asm->push(core::edx);
 253  E :      bb_asm->lea(core::edx, op);
 254  E :      bb_asm->call(Operand(Displacement(hook->referenced(), hook->offset())));
 255  E :    } else {
 256    :      // The special instruction probe take addresses directly in registers.
 257    :      // The probe doesn't have any effects on stack, registers and flags.
 258  E :      bb_asm->call(Operand(Displacement(hook->referenced(), hook->offset())));
 259    :    }
 260  E :  }
 261    :  
 262    :  typedef std::pair<BlockGraph::Block*, BlockGraph::Offset> ReferenceDest;
 263    :  typedef std::map<ReferenceDest, ReferenceDest> ReferenceMap;
 264    :  typedef std::set<BlockGraph::Block*> BlockSet;
 265    :  
 266    :  // For every block referencing @p dst_blocks, redirects any reference "ref" in
 267    :  // @p redirects to @p redirects[ref].
 268    :  void RedirectReferences(const BlockSet& dst_blocks,
 269  E :                          const ReferenceMap& redirects) {
 270    :    // For each block referenced by any source reference.
 271  E :    BlockSet::const_iterator dst_block_it = dst_blocks.begin();
 272  E :    for (; dst_block_it != dst_blocks.end(); ++dst_block_it) {
 273    :      // Iterate over all their referrers.
 274  E :      BlockGraph::Block* referred_block = *dst_block_it;
 275  E :      BlockGraph::Block::ReferrerSet referrers = referred_block->referrers();
 276  E :      BlockGraph::Block::ReferrerSet::iterator referrer_it = referrers.begin();
 277  E :      for (; referrer_it != referrers.end(); ++referrer_it) {
 278  E :        BlockGraph::Block* referrer = referrer_it->first;
 279    :  
 280    :        // Don't redirect references from PE parsed blocks. This actually ends up
 281    :        // redirecting the IAT entries as well in the worst case.
 282  E :        if (referrer->attributes() & BlockGraph::PE_PARSED)
 283  E :          continue;
 284    :  
 285    :        // And redirect any references that happen to match a source reference.
 286    :        BlockGraph::Block::ReferenceMap::const_iterator reference_it =
 287  E :            referrer->references().begin();
 288    :  
 289  E :        for (; reference_it != referrer->references().end(); ++reference_it) {
 290  E :          const BlockGraph::Reference& ref(reference_it->second);
 291  E :          ReferenceDest dest(std::make_pair(ref.referenced(), ref.offset()));
 292    :  
 293  E :          ReferenceMap::const_iterator it(redirects.find(dest));
 294  E :          if (it != redirects.end()) {
 295    :            BlockGraph::Reference new_reference(ref.type(),
 296    :                                                ref.size(),
 297    :                                                it->second.first,
 298    :                                                it->second.second,
 299  E :                                                0);
 300    :  
 301  E :            referrer->SetReference(reference_it->first, new_reference);
 302    :          }
 303  E :        }
 304  E :      }
 305  E :    }
 306  E :  }
 307    :  
 308    :  // Get the name of an asan check access function for an @p access_mode access.
 309    :  // @param info The memory access information, e.g. the size on a load/store,
 310    :  //     the instruction opcode and the kind of access.
 311    :  std::string GetAsanCheckAccessFunctionName(
 312  E :      AsanBasicBlockTransform::MemoryAccessInfo info) {
 313  E :    DCHECK(info.mode != AsanBasicBlockTransform::kNoAccess);
 314  E :    DCHECK(info.size != 0);
 315    :    DCHECK(info.mode == AsanBasicBlockTransform::kReadAccess ||
 316    :           info.mode == AsanBasicBlockTransform::kWriteAccess ||
 317  E :           info.opcode != 0);
 318    :  
 319  E :    const char* rep_str = NULL;
 320  E :    if (info.mode == AsanBasicBlockTransform::kRepzAccess)
 321  E :      rep_str = "_repz";
 322  E :    else if (info.mode == AsanBasicBlockTransform::kRepnzAccess)
 323  i :      rep_str = "_repnz";
 324  i :    else
 325  E :      rep_str = "";
 326    :  
 327  E :    const char* access_mode_str = NULL;
 328  E :    if (info.mode == AsanBasicBlockTransform::kReadAccess)
 329  E :      access_mode_str = "read";
 330  E :    else if (info.mode == AsanBasicBlockTransform::kWriteAccess)
 331  E :      access_mode_str = "write";
 332  E :    else
 333  E :      access_mode_str = reinterpret_cast<char*>(GET_MNEMONIC_NAME(info.opcode));
 334    :  
 335    :    std::string function_name =
 336    :        base::StringPrintf("asan_check%s_%d_byte_%s_access",
 337    :                            rep_str,
 338    :                            info.size,
 339  E :                            access_mode_str);
 340  E :    StringToLowerASCII(&function_name);
 341  E :    return function_name.c_str();
 342  E :  }
 343    :  
 344    :  // Add the imports for the asan check access hooks to the block-graph.
 345    :  // @param hooks_param_vector A vector of hook parameter values.
 346    :  // @param default_stub_map Stubs for the asan check access functions.
 347    :  // @param import_module The module for which the import should be added.
 348    :  // @param check_access_hook_map The map where the reference to the imports
 349    :  //     should be stored.
 350    :  // @param block_graph The block-graph to populate.
 351    :  // @param header_block The block containing the module's DOS header of this
 352    :  //     block-graph.
 353    :  // @returns True on success, false otherwise.
 354    :  bool AddAsanCheckAccessHooks(
 355    :      const AccessHookParamVector& hook_param_vector,
 356    :      const AsanBasicBlockTransform::AsanDefaultHookMap& default_stub_map,
 357    :      ImportedModule* import_module,
 358    :      HookMap* check_access_hook_map,
 359    :      BlockGraph* block_graph,
 360  E :      BlockGraph::Block* header_block) {
 361  E :    DCHECK(import_module != NULL);
 362  E :    DCHECK(check_access_hook_map != NULL);
 363  E :    DCHECK(block_graph != NULL);
 364  E :    DCHECK(header_block != NULL);
 365    :  
 366    :    // Add the hooks to the import module.
 367    :  
 368    :    typedef std::map<AsanBasicBlockTransform::AsanHookMapEntryKey, size_t>
 369    :        HooksParamsToIdxMap;
 370  E :    HooksParamsToIdxMap hooks_params_to_idx;
 371    :  
 372  E :    AccessHookParamVector::const_iterator iter_params = hook_param_vector.begin();
 373  E :    for (; iter_params != hook_param_vector.end(); ++iter_params) {
 374    :      size_t symbol_idx = import_module->AddSymbol(
 375    :          GetAsanCheckAccessFunctionName(*iter_params),
 376  E :          ImportedModule::kAlwaysImport);
 377  E :      hooks_params_to_idx[*iter_params] = symbol_idx;
 378  E :    }
 379    :  
 380  E :    DCHECK_EQ(hooks_params_to_idx.size(), hook_param_vector.size());
 381    :  
 382    :    // Transforms the block-graph.
 383    :  
 384  E :    AddImportsTransform add_imports_transform;
 385  E :    add_imports_transform.AddModule(import_module);
 386    :  
 387  E :    if (!add_imports_transform.TransformBlockGraph(block_graph, header_block)) {
 388  i :      LOG(ERROR) << "Unable to add imports for Asan instrumentation DLL.";
 389  i :      return false;
 390    :    }
 391    :  
 392    :    // Get a reference to each hook and put it in the hooks map.
 393  E :    HooksParamsToIdxMap::iterator iter_hooks = hooks_params_to_idx.begin();
 394  E :    for (; iter_hooks != hooks_params_to_idx.end(); ++iter_hooks) {
 395  E :      BlockGraph::Reference import_reference;
 396    :      if (!import_module->GetSymbolReference(iter_hooks->second,
 397  E :                                             &import_reference)) {
 398  i :        LOG(ERROR) << "Unable to get import reference for Asan.";
 399  i :        return false;
 400    :      }
 401  E :      HookMap& hook_map = *check_access_hook_map;
 402  E :      hook_map[iter_hooks->first] = import_reference;
 403    :  
 404    :      // In a Chrome sandboxed process the NtMapViewOfSection function is
 405    :      // intercepted by the sandbox agent. This causes execution in the executable
 406    :      // before imports have been resolved, as the ntdll patch invokes into the
 407    :      // executable while resolving imports. As the Asan instrumentation directly
 408    :      // refers to the IAT entries we need to temporarily stub these function
 409    :      // until the Asan imports are resolved. To do this we need to make the IAT
 410    :      // entries for those functions point to a temporarily block and we need to
 411    :      // mark the image import descriptor for this DLL as bound.
 412    :      AsanBasicBlockTransform::AsanDefaultHookMap::const_iterator stub_reference =
 413  E :          default_stub_map.find(iter_hooks->first.mode);
 414  E :      if (stub_reference == default_stub_map.end()) {
 415  i :         LOG(ERROR) << "Could not find the default hook for "
 416    :                    << GetAsanCheckAccessFunctionName(iter_hooks->first)
 417    :                    << ".";
 418  i :        return false;
 419    :      }
 420    :  
 421    :      import_reference.referenced()->SetReference(import_reference.offset(),
 422  E :                                                  stub_reference->second);
 423  E :    }
 424    :  
 425  E :    return true;
 426  E :  }
 427    :  
 428    :  // Create a stub for the asan_check_access functions. For load/store, the stub
 429    :  // consists of a small block of code that restores the value of EDX and returns
 430    :  // to the caller. Otherwise, the stub do return.
 431    :  // @param block_graph The block-graph to populate with the stub.
 432    :  // @param stub_name The stub's name.
 433    :  // @param mode The kind of memory access.
 434    :  // @param A pointer to the stub's block on success, NULL otherwise.
 435    :  // @param reference Will receive the reference to the created hook.
 436    :  // @returns true on success, false otherwise.
 437    :  bool CreateHooksStub(BlockGraph* block_graph,
 438    :                       const base::StringPiece stub_name,
 439    :                       AsanBasicBlockTransform::MemoryAccessMode mode,
 440  E :                       BlockGraph::Reference* reference) {
 441  E :    DCHECK(reference != NULL);
 442    :  
 443    :    // Find or create the section we put our thunks in.
 444    :    BlockGraph::Section* thunk_section = block_graph->FindOrAddSection(
 445  E :        common::kThunkSectionName, pe::kCodeCharacteristics);
 446    :  
 447  E :    if (thunk_section == NULL) {
 448  i :      LOG(ERROR) << "Unable to find or create .thunks section.";
 449  i :      return false;
 450    :    }
 451    :  
 452  E :    std::string stub_name_with_id = base::StringPrintf("%s%d", stub_name, mode);
 453    :  
 454    :    // Create the thunk for standard "load/store" (received address in EDX).
 455  E :    BasicBlockSubGraph bbsg;
 456    :    BasicBlockSubGraph::BlockDescription* block_desc = bbsg.AddBlockDescription(
 457  E :        stub_name_with_id, BlockGraph::CODE_BLOCK, thunk_section->id(), 1, 0);
 458    :  
 459  E :    BasicCodeBlock* bb = bbsg.AddBasicCodeBlock(stub_name_with_id);
 460  E :    block_desc->basic_block_order.push_back(bb);
 461  E :    BasicBlockAssembler assm(bb->instructions().begin(), &bb->instructions());
 462    :  
 463    :    if (mode == AsanBasicBlockTransform::kReadAccess ||
 464  E :        mode == AsanBasicBlockTransform::kWriteAccess) {
 465    :      // The thunk body restores the original value of EDX and cleans the stack on
 466    :      // return.
 467  E :      assm.mov(core::edx, Operand(core::esp, Displacement(4)));
 468  E :      assm.ret(4);
 469  E :    } else {
 470  E :      assm.ret();
 471    :    }
 472    :  
 473    :    // Condense into a block.
 474  E :    BlockBuilder block_builder(block_graph);
 475  E :    if (!block_builder.Merge(&bbsg)) {
 476  i :      LOG(ERROR) << "Failed to build thunk block.";
 477  i :      return NULL;
 478    :    }
 479    :  
 480    :    // Exactly one new block should have been created.
 481  E :    DCHECK_EQ(1u, block_builder.new_blocks().size());
 482  E :    BlockGraph::Block* thunk = block_builder.new_blocks().front();
 483    :  
 484  E :    *reference = BlockGraph::Reference(BlockGraph::ABSOLUTE_REF, 4, thunk, 0, 0);
 485    :  
 486  E :    return true;
 487  E :  }
 488    :  
 489    :  }  // namespace
 490    :  
 491    :  const char AsanBasicBlockTransform::kTransformName[] =
 492    :      "SyzyAsanBasicBlockTransform";
 493    :  
 494    :  bool AsanBasicBlockTransform::InstrumentBasicBlock(
 495  E :      BasicCodeBlock* basic_block) {
 496  E :    DCHECK(basic_block != NULL);
 497    :    BasicBlock::Instructions::iterator iter_inst =
 498  E :        basic_block->instructions().begin();
 499    :  
 500    :    // Process each instruction and inject a call to Asan when we find an
 501    :    // instrumentable memory access.
 502  E :    for (; iter_inst != basic_block->instructions().end(); ++iter_inst) {
 503  E :      Operand operand(core::eax);
 504  E :      const Instruction& instr = *iter_inst;
 505  E :      const _DInst& repr = instr.representation();
 506    :  
 507    :      MemoryAccessInfo info;
 508  E :      info.mode = kNoAccess;
 509  E :      info.size = 0;
 510  E :      info.opcode = 0;
 511    :  
 512    :      // Insert hook for a standard instruction.
 513  E :      if (!DecodeMemoryAccess(instr, &operand, &info))
 514  E :        continue;
 515    :  
 516    :      // Bail if this is not a memory access.
 517  E :      if (info.mode == kNoAccess)
 518  i :        continue;
 519    :  
 520    :      // A basic block reference means that can be either a computed jump,
 521    :      // or a load from a case table. In either case it doesn't make sense
 522    :      // to instrument the access.
 523    :      if (operand.displacement().reference().referred_type() ==
 524  E :          BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK) {
 525  E :        continue;
 526    :      }
 527    :  
 528    :      // A block reference means this instruction is reading or writing to
 529    :      // a global variable or some such. It's viable to pad and align global
 530    :      // variables and to red-zone the padding, but without that, there's nothing
 531    :      // to gain by instrumenting these accesses.
 532    :      if (operand.displacement().reference().referred_type() ==
 533  E :          BasicBlockReference::REFERRED_TYPE_BLOCK) {
 534  E :        continue;
 535    :      }
 536    :  
 537    :      // Is this an instruction we should be instrumenting.
 538  E :      if (!ShouldInstrumentOpcode(repr.opcode))
 539  E :        continue;
 540    :  
 541    :      // We do not instrument stack-based accesses. These include accesses based
 542    :      // on ESP or EBP (which is usually the stack frame base pointer (like ESP
 543    :      // or a scalar non-pointer value). We have never seen EBP used as a pointer.
 544    :      if (operand.base() == core::kRegisterEsp ||
 545  E :          operand.base() == core::kRegisterEbp) {
 546  E :        continue;
 547    :      }
 548    :  
 549    :      // We do not instrument memory accesses through special segments.
 550    :      // FS is used for thread local specifics and GS for CPU info.
 551  E :      uint8_t segment = SEGMENT_GET(repr.segment);
 552  E :      if (segment == R_FS || segment == R_GS)
 553  E :        continue;
 554    :  
 555    :      // Finally, don't instrument any filtered instructions.
 556  E :      if (IsFiltered(*iter_inst))
 557  E :        continue;
 558    :  
 559    :      // Create a BasicBlockAssembler to insert new instruction.
 560  E :      BasicBlockAssembler bb_asm(iter_inst, &basic_block->instructions());
 561    :  
 562    :      // Configure the assembler to copy the SourceRange information of the
 563    :      // current instrumented instruction into newly created instructions. This is
 564    :      // a hack to allow valid stack walking and better error reporting, but
 565    :      // breaks the 1:1 OMAP mapping and may confuse some debuggers.
 566  E :      bb_asm.set_source_range(instr.source_range());
 567    :  
 568    :      // Insert hook for standard instructions.
 569  E :      AsanHookMap::iterator hook = check_access_hooks_->find(info);
 570  E :      if (hook == check_access_hooks_->end()) {
 571  i :        LOG(ERROR) << "Invalid access : " << GetAsanCheckAccessFunctionName(info);
 572  i :        return false;
 573    :      }
 574  E :      InjectAsanHook(&bb_asm, info, operand, &hook->second);
 575  E :    }
 576  E :    return true;
 577  E :  }
 578    :  
 579    :  bool AsanBasicBlockTransform::TransformBasicBlockSubGraph(
 580  E :      BlockGraph* block_graph, BasicBlockSubGraph* subgraph) {
 581  E :    DCHECK(block_graph != NULL);
 582  E :    DCHECK(subgraph != NULL);
 583    :  
 584    :    // Iterates through each basic block and instruments it.
 585    :    BasicBlockSubGraph::BBCollection::iterator it =
 586  E :        subgraph->basic_blocks().begin();
 587  E :    for (; it != subgraph->basic_blocks().end(); ++it) {
 588  E :      BasicCodeBlock* bb = BasicCodeBlock::Cast(*it);
 589  E :      if (bb != NULL && !InstrumentBasicBlock(bb))
 590  i :        return false;
 591  E :    }
 592  E :    return true;
 593  E :  }
 594    :  
 595    :  const char AsanTransform::kTransformName[] = "SyzyAsanTransform";
 596    :  
 597    :  const char AsanTransform::kAsanHookStubName[] = "asan_hook_stub";
 598    :  
 599    :  const char AsanTransform::kSyzyAsanDll[] = "asan_rtl.dll";
 600    :  
 601  E :  AsanTransform::AsanTransform() : asan_dll_name_(kSyzyAsanDll) {
 602  E :  }
 603    :  
 604    :  bool AsanTransform::PreBlockGraphIteration(BlockGraph* block_graph,
 605  E :                                             BlockGraph::Block* header_block) {
 606  E :    bool already_instrumented = false;
 607    :    // Ensure that this image has not already been instrumented.
 608  E :    if (!pe::HasImportEntry(header_block, kSyzyAsanDll, &already_instrumented)) {
 609  i :      LOG(ERROR) << "Unable to check if the image is already instrumented.";
 610  i :      return false;
 611    :    }
 612    :  
 613  E :    if (already_instrumented) {
 614  i :      LOG(ERROR) << "The image is already instrumented.";
 615  i :      return false;
 616    :    }
 617    :  
 618  E :    AccessHookParamVector access_hook_param_vec;
 619  E :    AsanBasicBlockTransform::AsanDefaultHookMap default_stub_map;
 620    :  
 621    :    // Create the hook stub for read/write instructions.
 622  E :    BlockGraph::Reference read_write_hook;
 623    :    if (!CreateHooksStub(block_graph, kAsanHookStubName,
 624    :                         AsanBasicBlockTransform::kReadAccess,
 625  E :                        &read_write_hook)) {
 626  i :      return false;
 627    :    }
 628    :  
 629    :    // Create the hook stub for strings instructions.
 630  E :    BlockGraph::Reference instr_hook;
 631    :    if (!CreateHooksStub(block_graph, kAsanHookStubName,
 632    :                         AsanBasicBlockTransform::kInstrAccess,
 633  E :                        &instr_hook)) {
 634  i :      return false;
 635    :    }
 636    :  
 637    :    // Map each memory access kind to a appropriate stub.
 638  E :    default_stub_map[AsanBasicBlockTransform::kReadAccess] = read_write_hook;
 639  E :    default_stub_map[AsanBasicBlockTransform::kWriteAccess] = read_write_hook;
 640  E :    default_stub_map[AsanBasicBlockTransform::kInstrAccess] = instr_hook;
 641  E :    default_stub_map[AsanBasicBlockTransform::kRepzAccess] = instr_hook;
 642  E :    default_stub_map[AsanBasicBlockTransform::kRepnzAccess] = instr_hook;
 643    :  
 644    :    // Add an import entry for the ASAN runtime.
 645  E :    ImportedModule import_module(asan_dll_name_);
 646    :  
 647    :    // Import the hooks for the read/write accesses.
 648  E :    for (int access_size = 1; access_size <= 32; access_size *= 2) {
 649    :      MemoryAccessInfo read_info =
 650  E :          { AsanBasicBlockTransform::kReadAccess, access_size, 0 };
 651  E :      access_hook_param_vec.push_back(read_info);
 652    :  
 653    :      MemoryAccessInfo write_info =
 654  E :          { AsanBasicBlockTransform::kWriteAccess, access_size, 0 };
 655  E :      access_hook_param_vec.push_back(write_info);
 656  E :    }
 657    :  
 658    :    // Import the hooks for the read/write 10-bytes accesses.
 659    :    MemoryAccessInfo read_info_10 =
 660  E :        { AsanBasicBlockTransform::kReadAccess, 10, 0 };
 661  E :    access_hook_param_vec.push_back(read_info_10);
 662    :  
 663    :    MemoryAccessInfo write_info_10 =
 664  E :        { AsanBasicBlockTransform::kWriteAccess, 10, 0 };
 665  E :    access_hook_param_vec.push_back(write_info_10);
 666    :  
 667    :    // Import the hooks for strings/prefix memory accesses.
 668  E :    const _InstructionType strings[] = { I_CMPS, I_MOVS, I_STOS };
 669  E :    int strings_length = sizeof(strings)/sizeof(_InstructionType);
 670    :  
 671  E :    for (int access_size = 1; access_size <= 4; access_size *= 2) {
 672  E :      for (int inst = 0; inst < strings_length; ++inst) {
 673    :        MemoryAccessInfo repz_inst_info =
 674  E :            { AsanBasicBlockTransform::kRepzAccess, access_size, strings[inst] };
 675  E :        access_hook_param_vec.push_back(repz_inst_info);
 676    :  
 677    :        MemoryAccessInfo inst_info =
 678  E :            { AsanBasicBlockTransform::kInstrAccess, access_size, strings[inst] };
 679  E :        access_hook_param_vec.push_back(inst_info);
 680  E :      }
 681  E :    }
 682    :  
 683    :    if (!AddAsanCheckAccessHooks(access_hook_param_vec,
 684    :                                 default_stub_map,
 685    :                                 &import_module,
 686    :                                 &check_access_hooks_ref_,
 687    :                                 block_graph,
 688  E :                                 header_block)) {
 689  i :      return false;
 690    :    }
 691  E :    return true;
 692  E :  }
 693    :  
 694    :  bool AsanTransform::OnBlock(BlockGraph* block_graph,
 695  E :                              BlockGraph::Block* block) {
 696  E :    DCHECK(block_graph != NULL);
 697  E :    DCHECK(block != NULL);
 698  E :    if (block->type() != BlockGraph::CODE_BLOCK)
 699  E :      return true;
 700    :  
 701  E :    if (!pe::CodeBlockIsBasicBlockDecomposable(block))
 702  E :      return true;
 703    :  
 704    :    // Use the filter that was passed to us for our child transform.
 705  E :    AsanBasicBlockTransform transform(&check_access_hooks_ref_);
 706  E :    transform.set_filter(filter());
 707    :  
 708  E :    if (!ApplyBasicBlockSubGraphTransform(&transform, block_graph, block, NULL))
 709  i :      return false;
 710    :  
 711  E :    return true;
 712  E :  }
 713    :  
 714    :  bool AsanTransform::PostBlockGraphIteration(BlockGraph* block_graph,
 715  E :                                              BlockGraph::Block* header_block) {
 716    :    // This function redirects a the heap-related kernel32 imports to point to
 717    :    // a set of "override" imports in the ASAN runtime.
 718    :  
 719    :    static const size_t kInvalidIndex = -1;
 720    :  
 721    :    struct Kernel32ImportRedirect {
 722    :      const char* import_name;
 723    :      const char* redirect_name;
 724    :    };
 725    :    static const Kernel32ImportRedirect kKernel32Redirects[] = {
 726    :      { "HeapCreate", "asan_HeapCreate" },
 727    :      { "HeapDestroy", "asan_HeapDestroy" },
 728    :      { "HeapAlloc", "asan_HeapAlloc" },
 729    :      { "HeapReAlloc", "asan_HeapReAlloc" },
 730    :      { "HeapFree", "asan_HeapFree" },
 731    :      { "HeapSize", "asan_HeapSize" },
 732    :      { "HeapValidate", "asan_HeapValidate" },
 733    :      { "HeapCompact", "asan_HeapCompact" },
 734    :      { "HeapLock", "asan_HeapLock" },
 735    :      { "HeapUnlock", "asan_HeapUnlock" },
 736    :      { "HeapWalk", "asan_HeapWalk" },
 737    :      { "HeapSetInformation", "asan_HeapSetInformation" },
 738    :      { "HeapQueryInformation", "asan_HeapQueryInformation" },
 739    :    };
 740    :  
 741    :    // Initialize the module info for querying kernel32 imports.
 742  E :    std::vector<std::pair<size_t, size_t>> override_indexes;
 743  E :    ImportedModule module_kernel32("kernel32.dll");
 744  E :    for (size_t i = 0; i < arraysize(kKernel32Redirects); ++i) {
 745    :      size_t kernel32_index =
 746    :          module_kernel32.AddSymbol(kKernel32Redirects[i].import_name,
 747  E :                                    ImportedModule::kFindOnly);
 748  E :      override_indexes.push_back(std::make_pair(kernel32_index, kInvalidIndex));
 749  E :    }
 750    :  
 751    :    // Query the kernel32 imports.
 752  E :    AddImportsTransform find_kernel_imports;
 753  E :    find_kernel_imports.AddModule(&module_kernel32);
 754  E :    if (!find_kernel_imports.TransformBlockGraph(block_graph, header_block)) {
 755  i :      LOG(ERROR) << "Unable to find kernel32 imports for redirection.";
 756  i :      return false;
 757    :    }
 758    :  
 759    :    // Add ASAN imports for those kernel32 functions we found. These will later
 760    :    // be redirected.
 761  E :    ImportedModule module_asan(asan_dll_name_);
 762  E :    for (size_t i = 0; i < arraysize(kKernel32Redirects); ++i) {
 763  E :      size_t kernel32_index = override_indexes[i].first;
 764  E :      if (module_kernel32.SymbolIsImported(kernel32_index)) {
 765    :        size_t asan_index = module_asan.AddSymbol(
 766    :            kKernel32Redirects[i].redirect_name,
 767  E :            ImportedModule::kAlwaysImport);
 768  E :        DCHECK_EQ(kInvalidIndex, override_indexes[i].second);
 769  E :        override_indexes[i].second = asan_index;
 770    :      }
 771  E :    }
 772    :  
 773    :    // Another transform can safely be run without invalidating the results
 774    :    // stored in module_kernel32, as additions to the IAT will strictly be
 775    :    // performed at the end.
 776  E :    AddImportsTransform add_imports_transform;
 777  E :    add_imports_transform.AddModule(&module_asan);
 778  E :    if (!add_imports_transform.TransformBlockGraph(block_graph, header_block)) {
 779  i :      LOG(ERROR) << "Unable to add imports for import redirection.";
 780  i :      return false;
 781    :    }
 782    :  
 783    :    // Keeps track of all the blocks referenced by the original references.
 784  E :    BlockSet dst_blocks;
 785    :    // Stores the reference mapping we want to rewrite.
 786  E :    ReferenceMap reference_redirect_map;
 787    :  
 788  E :    for (size_t i = 0; i < override_indexes.size(); ++i) {
 789    :      // Symbols that aren't imported don't need to be redirected.
 790  E :      size_t kernel32_index = override_indexes[i].first;
 791  E :      size_t asan_index = override_indexes[i].second;
 792  E :      if (!module_kernel32.SymbolIsImported(kernel32_index)) {
 793  E :        DCHECK_EQ(kInvalidIndex, asan_index);
 794  E :        continue;
 795    :      }
 796    :  
 797  E :      DCHECK_NE(kInvalidIndex, asan_index);
 798  E :      BlockGraph::Reference src;
 799  E :      BlockGraph::Reference dst;
 800    :      if (!module_kernel32.GetSymbolReference(kernel32_index, &src) ||
 801  E :          !module_asan.GetSymbolReference(asan_index, &dst)) {
 802  i :         NOTREACHED() << "Unable to get references after a successful transform.";
 803  i :        return false;
 804    :      }
 805    :  
 806    :      // Add the destination block to the set of referred blocks.
 807  E :      dst_blocks.insert(src.referenced());
 808    :      reference_redirect_map.insert(
 809    :          std::make_pair(ReferenceDest(src.referenced(), src.offset()),
 810  E :                         ReferenceDest(dst.referenced(), dst.offset())));
 811  E :    }
 812    :  
 813  E :    RedirectReferences(dst_blocks, reference_redirect_map);
 814    :  
 815    :    // The timestamp 1 corresponds to Thursday, 01 Jan 1970 00:00:01 GMT. Setting
 816    :    // the timestamp of the image import descriptor to this value allows us to
 817    :    // temporarily bind the library until the loader finishes loading this module.
 818    :    // As the value is far in the past this means that the entries in the IAT for
 819    :    // this module will all be replace by pointers into the actual library.
 820    :    static const size_t kDateInThePast = 1;
 821    :  
 822    :    // We need to bind the IAT for our module to make sure the stub is used until
 823    :    // the sandbox lets the loader finish patching the IAT entries.
 824  E :    module_asan.import_descriptor()->TimeDateStamp = kDateInThePast;
 825    :  
 826  E :    return true;
 827  E :  }
 828    :  
 829    :  bool operator<(const AsanBasicBlockTransform::MemoryAccessInfo& left,
 830  E :                 const AsanBasicBlockTransform::MemoryAccessInfo& right) {
 831  E :    if (left.mode != right.mode)
 832  E :      return left.mode < right.mode;
 833  E :    if (left.size != right.size)
 834  E :      return left.size < right.size;
 835  E :    return left.opcode < right.opcode;
 836  E :  }
 837    :  
 838    :  }  // namespace transforms
 839    :  }  // namespace instrument

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