Coverage for /Syzygy/agent/asan/iat_patcher.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
74.1%40540.C++source

Line-by-line coverage:

   1    :  // Copyright 2015 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/agent/asan/iat_patcher.h"
  16    :  
  17    :  #include <vector>
  18    :  
  19    :  #include "base/logging.h"
  20    :  #include "base/win/iat_patch_function.h"
  21    :  #include "base/win/pe_image.h"
  22    :  #include "syzygy/agent/asan/scoped_page_protections.h"
  23    :  #include "syzygy/common/align.h"
  24    :  #include "syzygy/common/com_utils.h"
  25    :  
  26    :  namespace agent {
  27    :  namespace asan {
  28    :  
  29    :  namespace {
  30    :  
  31    :  using OnUnprotectCallback = ScopedPageProtections::OnUnprotectCallback;
  32    :  
  33    :  PatchResult UpdateImportThunk(volatile PIMAGE_THUNK_DATA iat,
  34  i :                                FunctionPointer function) {
  35    :    // Writing to an IAT is inherently racy, as there may be other parties also
  36    :    // writing the same page at the same time. This gets ugly where multiple
  37    :    // parties mess with page protections, as VirtualProtect causes surprising
  38    :    // serialization. We therefore proceed with an abundance of caution, by
  39    :    // running inside an exception handler and using a compare-and-swap to
  40    :    // detect races on VM operations as well as on assignment.
  41    :  
  42  i :    __try {
  43  i :      DWORD old_fn = iat->u1.Function;
  44  i :      DWORD new_fn = reinterpret_cast<DWORD>(function);
  45    :      DWORD prev_fn =
  46  i :          ::InterlockedCompareExchange(&iat->u1.Function, new_fn, old_fn);
  47    :      // Check whether we collided on the assignment.
  48  i :      if (prev_fn != old_fn)
  49  i :        return PATCH_FAILED_RACY_WRITE;
  50  i :    } __except(EXCEPTION_EXECUTE_HANDLER) {
  51    :      // We took an exception, that goes down as failure. This can occur if we
  52    :      // are racing with another thread in our process to patch the IAT entries
  53    :      // in the same physical page.
  54  i :      return PATCH_FAILED_ACCESS_VIOLATION;
  55    :    }
  56    :  
  57    :    // All shiny!
  58  i :    return PATCH_SUCCEEDED;
  59  i :  }
  60    :  
  61    :  class IATPatchWorker {
  62    :   public:
  63    :    explicit IATPatchWorker(const IATPatchMap& patch);
  64    :  
  65    :    PatchResult PatchImage(base::win::PEImage* image);
  66    :  
  67  E :    void set_on_unprotect(OnUnprotectCallback on_unprotect) {
  68  E :      scoped_page_protections_.set_on_unprotect(on_unprotect);
  69  E :    }
  70    :  
  71    :   private:
  72    :    static bool VisitImport(const base::win::PEImage &image, LPCSTR module,
  73    :                            DWORD ordinal, LPCSTR name, DWORD hint,
  74    :                            PIMAGE_THUNK_DATA iat, PVOID cookie);
  75    :    PatchResult OnImport(const char* name, PIMAGE_THUNK_DATA iat);
  76    :  
  77    :    ScopedPageProtections scoped_page_protections_;
  78    :    const IATPatchMap& patch_;
  79    :    PatchResult result_;
  80    :  
  81    :    DISALLOW_COPY_AND_ASSIGN(IATPatchWorker);
  82    :  };
  83    :  
  84    :  IATPatchWorker::IATPatchWorker(const IATPatchMap& patch)
  85  E :      : patch_(patch), result_(PATCH_SUCCEEDED) {
  86  E :  }
  87    :  
  88  E :  PatchResult IATPatchWorker::PatchImage(base::win::PEImage* image) {
  89  E :    DCHECK_NE(static_cast<base::win::PEImage*>(nullptr), image);
  90    :  
  91    :    // This is actually '0', so ORing error conditions to it is just fine.
  92  E :    result_ = PATCH_SUCCEEDED;
  93    :  
  94    :    // The IAT patching takes place during enumeration.
  95  E :    image->EnumAllImports(&VisitImport, this);
  96    :  
  97    :    // Clean up whatever we soiled, success or failure be damned.
  98  E :    if (!scoped_page_protections_.RestorePageProtections())
  99  i :      result_ |= PATCH_FAILED_REPROTECT_FAILED;
 100    :  
 101  E :    return result_;
 102  E :  }
 103    :  
 104    :  bool IATPatchWorker::VisitImport(
 105    :      const base::win::PEImage &image, LPCSTR module, DWORD ordinal,
 106  E :      LPCSTR name, DWORD hint, PIMAGE_THUNK_DATA iat, PVOID cookie) {
 107  E :    if (!name)
 108  E :      return true;
 109    :  
 110  E :    IATPatchWorker* worker = reinterpret_cast<IATPatchWorker*>(cookie);
 111  E :    PatchResult result = worker->OnImport(name, iat);
 112  E :    if (result == PATCH_SUCCEEDED)
 113  E :      return true;
 114    :  
 115    :    // Remember the reason for failure.
 116  E :    worker->result_ |= result;
 117  E :    return false;
 118  E :  }
 119    :  
 120  E :  PatchResult IATPatchWorker::OnImport(const char* name, PIMAGE_THUNK_DATA iat) {
 121  E :    auto it = patch_.find(name);
 122    :    // See whether this is a function we care about.
 123  E :    if (it == patch_.end())
 124  E :      return PATCH_SUCCEEDED;
 125    :  
 126    :    // Make the containing page writable.
 127  E :    if (!scoped_page_protections_.EnsureContainingPagesWritable(
 128    :            iat, sizeof(IMAGE_THUNK_DATA))) {
 129  i :      return PATCH_FAILED_UNPROTECT_FAILED;
 130    :    }
 131    :  
 132  E :    return UpdateImportThunk(iat, it->second);
 133  E :  }
 134    :  
 135    :  }  // namespace
 136    :  
 137  E :  PatchResult PatchIATForModule(HMODULE module, const IATPatchMap& patch_map) {
 138  E :    OnUnprotectCallback dummy_on_unprotect;
 139  E :    return PatchIATForModule(module, patch_map, dummy_on_unprotect);
 140  E :  }
 141    :  
 142    :  PatchResult PatchIATForModule(HMODULE module, const IATPatchMap& patch_map,
 143  E :      OnUnprotectCallback on_unprotect) {
 144  E :    base::win::PEImage image(module);
 145  E :    if (!image.VerifyMagic())
 146  i :      return PATCH_FAILED_INVALID_IMAGE;
 147    :  
 148  E :    IATPatchWorker worker(patch_map);
 149  E :    worker.set_on_unprotect(on_unprotect);
 150  E :    return worker.PatchImage(&image);
 151  E :  }
 152    :  
 153    :  }  // namespace asan
 154    :  }  // namespace agent

Coverage information generated Fri Jul 29 11:00:21 2016.