Coverage for /Syzygy/kasko/api/internal/crash_key_registration.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
77.6%831070.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/kasko/api/internal/crash_key_registration.h"
  16    :  
  17    :  #include <psapi.h>
  18    :  
  19    :  #include <string.h>
  20    :  
  21    :  #include <cstdint>
  22    :  
  23    :  #include "base/logging.h"
  24    :  #include "base/process/process_handle.h"
  25    :  #include "base/strings/string16.h"
  26    :  #include "syzygy/common/process_utils.h"
  27    :  #include "syzygy/kasko/api/crash_key.h"
  28    :  
  29    :  // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
  30    :  extern "C" IMAGE_DOS_HEADER __ImageBase;
  31    :  
  32    :  namespace kasko {
  33    :  namespace api {
  34    :  namespace internal {
  35    :  
  36    :  namespace {
  37    :  
  38    :  // Used to store the CrashKey array address and size in the client process.
  39    :  struct CrashKeyStorage {
  40    :    const CrashKey* crash_keys;
  41    :    size_t crash_key_count;
  42    :  } g_crash_key_storage = {nullptr, 0};
  43    :  
  44    :  // Returns the image path of |module| in |process|.
  45  E :  base::string16 GetModulePath(HANDLE process, HMODULE module) {
  46    :    base::char16 path_buffer[MAX_PATH];
  47    :    DWORD bytes_read = ::GetModuleFileNameEx(process, module, path_buffer,
  48  E :                                             arraysize(path_buffer));
  49  E :    if (bytes_read == 0)
  50  i :      DPLOG(ERROR) << "GetModuleFileNameEx";
  51  E :    else if (bytes_read >= arraysize(path_buffer))
  52  i :      return base::string16();
  53    :  
  54  E :    return path_buffer;
  55  E :  }
  56    :  
  57    :  // Returns the image path of the current module.
  58  E :  base::string16 GetCurrentModulePath() {
  59    :    return GetModulePath(base::GetCurrentProcessHandle(),
  60  E :                         reinterpret_cast<HMODULE>(&__ImageBase));
  61  E :  }
  62    :  
  63    :  // Returns the linker timestamp of the current module.
  64  E :  DWORD GetCurrentModuleTimestamp() {
  65    :    uintptr_t image_dos_header_address =
  66  E :        reinterpret_cast<uintptr_t>(&__ImageBase);
  67    :    IMAGE_DOS_HEADER* image_dos_header =
  68  E :        reinterpret_cast<IMAGE_DOS_HEADER*>(image_dos_header_address);
  69    :    IMAGE_NT_HEADERS* image_nt_header = reinterpret_cast<IMAGE_NT_HEADERS*>(
  70  E :        image_dos_header_address + image_dos_header->e_lfanew);
  71  E :    return image_nt_header->FileHeader.TimeDateStamp;
  72  E :  }
  73    :  
  74    :  // Returns the size of |module| in |process|.
  75  E :  DWORD GetModuleSize(HANDLE process, HMODULE module) {
  76  E :    MODULEINFO module_info = {0};
  77    :    if (!::GetModuleInformation(process, module, &module_info,
  78  E :                                sizeof(module_info))) {
  79  i :      DPLOG(ERROR) << "GetModuleInformation";
  80  i :      return 0;
  81    :    }
  82  E :    DCHECK_NE(0u, module_info.SizeOfImage);
  83  E :    return module_info.SizeOfImage;
  84  E :  }
  85    :  
  86    :  // Returns the size of the current module.
  87  E :  DWORD GetCurrentModuleSize() {
  88    :    return GetModuleSize(base::GetCurrentProcessHandle(),
  89  E :                         reinterpret_cast<HMODULE>(&__ImageBase));
  90  E :  }
  91    :  
  92    :  // Reads a value of type T from |address| in |process| into |value|. Returns
  93    :  // true if successful.
  94    :  template <typename T>
  95  E :  bool ReadValueFromOtherProcess(HANDLE process, uintptr_t address, T* value) {
  96  E :    DWORD bytes_count = 0;
  97    :    if (!::ReadProcessMemory(process, reinterpret_cast<void*>(address), value,
  98  E :                             sizeof(T), &bytes_count)) {
  99  i :      DPLOG(ERROR) << "ReadProcessMemory";
 100  i :      return false;
 101    :    }
 102  E :    if (bytes_count != sizeof(T))
 103  i :      return false;
 104  E :    return true;
 105  E :  }
 106    :  
 107    :  // Reads the crash keys from another instance of the current module image,
 108    :  // loaded into another process.
 109    :  bool ReadCrashKeysFromProcessModule(HANDLE process,
 110    :                                      HMODULE module,
 111  E :                                      std::vector<CrashKey>* crash_keys) {
 112    :    // Calculate the offset of g_crash_key_storage from our base address. It will
 113    :    // be the same in the other instance.
 114    :    ptrdiff_t storage_offset = reinterpret_cast<uintptr_t>(&g_crash_key_storage) -
 115  E :                               reinterpret_cast<uintptr_t>(&__ImageBase);
 116    :  
 117    :    // Calculate the virtual address of g_crash_key_storage in the other instance.
 118    :    uintptr_t storage_address =
 119  E :        storage_offset + reinterpret_cast<uintptr_t>(module);
 120    :  
 121    :    // Read the CrashKeyStorage structure, which contains the address and size of
 122    :    // a CrashKey array in the other process.
 123  E :    CrashKeyStorage crash_key_storage = {0};
 124  E :    if (!ReadValueFromOtherProcess(process, storage_address, &crash_key_storage))
 125  i :      return false;
 126    :  
 127    :    // Prepare a buffer and read the CrashKey array into it.
 128  E :    crash_keys->resize(crash_key_storage.crash_key_count);
 129  E :    DWORD bytes_count = 0;
 130    :    if (!::ReadProcessMemory(
 131    :            process, crash_key_storage.crash_keys, crash_keys->data(),
 132  E :            sizeof(CrashKey) * crash_keys->size(), &bytes_count)) {
 133  i :      DPLOG(ERROR) << "ReadProcessMemory";
 134  i :      crash_keys->clear();
 135  i :      return false;
 136    :    }
 137  E :    if (bytes_count != sizeof(CrashKey) * crash_keys->size()) {
 138  i :      crash_keys->clear();
 139  i :      return false;
 140    :    }
 141    :  
 142    :    // Validate the CrashKey array that we read. If any of the names or values is
 143    :    // not properly terminated, fail the entire operation.
 144  E :    for (const auto& crash_key : *crash_keys) {
 145    :      if (::wcsnlen(crash_key.name, CrashKey::kNameMaxLength) ==
 146  E :          CrashKey::kNameMaxLength) {
 147  i :        crash_keys->clear();
 148  i :        return false;
 149    :      }
 150    :      if (::wcsnlen(crash_key.value, CrashKey::kValueMaxLength) ==
 151  E :          CrashKey::kValueMaxLength) {
 152  i :        crash_keys->clear();
 153  i :        return false;
 154    :      }
 155  E :    }
 156  E :    return true;
 157  E :  }
 158    :  
 159    :  // Retrieves the linker timestamp from a module loaded into another process.
 160  E :  DWORD GetOtherModuleTimestamp(HANDLE process, HANDLE module) {
 161    :    // Read the relative address of the NT_IMAGE_HEADERS structure from the
 162    :    // IMAGE_DOS_HEADER (which is located at the module's base address).
 163  E :    decltype(IMAGE_DOS_HEADER::e_lfanew) nt_header_offset = 0;
 164    :    if (!ReadValueFromOtherProcess(process,
 165    :                                   reinterpret_cast<uintptr_t>(module) +
 166    :                                       offsetof(IMAGE_DOS_HEADER, e_lfanew),
 167  E :                                   &nt_header_offset)) {
 168  i :      return 0;
 169    :    }
 170  E :    if (nt_header_offset == 0)
 171  i :      return 0;
 172    :  
 173    :    // Calculate the address of the timestamp, which is stored in
 174    :    // image_nt_header.FileHeader.TimeDateStamp.
 175    :    uintptr_t image_nt_header_address =
 176  E :        reinterpret_cast<uintptr_t>(module) + nt_header_offset;
 177    :    uintptr_t image_file_header_address =
 178  E :        image_nt_header_address + offsetof(IMAGE_NT_HEADERS, FileHeader);
 179    :    uintptr_t time_date_stamp_address =
 180  E :        image_file_header_address + offsetof(IMAGE_FILE_HEADER, TimeDateStamp);
 181    :  
 182    :    // Read the value of the timestamp.
 183  E :    decltype(IMAGE_FILE_HEADER::TimeDateStamp) time_date_stamp = 0;
 184    :    if (!ReadValueFromOtherProcess(process, time_date_stamp_address,
 185  E :                                   &time_date_stamp)) {
 186  i :      return 0;
 187    :    }
 188    :  
 189  E :    return time_date_stamp;
 190  E :  }
 191    :  
 192    :  // Reads a fingerprint of the current module, and provides a method to compare
 193    :  // that fingerprint to a module in another process.
 194    :  class CurrentModuleMatcher {
 195    :   public:
 196    :    CurrentModuleMatcher()
 197    :        : path_(GetCurrentModulePath()),
 198    :          timestamp_(GetCurrentModuleTimestamp()),
 199  E :          size_(GetCurrentModuleSize()) {}
 200    :  
 201    :    // Returns true if the specifed |module| in |process| appears to be the same
 202    :    // image as the current module.
 203  E :    bool Matches(HANDLE process, HMODULE module) {
 204    :      // Give up now if we failed to read any of our own identifying values. As a
 205    :      // result, we know we will fail later if we fail to read one of the other
 206    :      // module's values.
 207  E :      if (path_.empty() || timestamp_ == 0 || size_ == 0)
 208  i :        return false;
 209    :  
 210  E :      base::string16 other_path = GetModulePath(process, module);
 211  E :      if (other_path != path_)
 212  E :        return false;
 213    :  
 214  E :      DWORD other_size = GetModuleSize(process, module);
 215  E :      if (other_size != size_)
 216  i :        return false;
 217    :  
 218  E :      DWORD other_timestamp = GetOtherModuleTimestamp(process, module);
 219  E :      if (other_timestamp != timestamp_)
 220  i :        return false;
 221    :  
 222  E :      return true;
 223  E :    }
 224    :  
 225    :   private:
 226    :    base::string16 path_;
 227    :    DWORD timestamp_;
 228    :    DWORD size_;
 229    :  };
 230    :  
 231    :  }  // namespace
 232    :  
 233  E :  void RegisterCrashKeys(const CrashKey* crash_keys, size_t count) {
 234  E :    DCHECK(!g_crash_key_storage.crash_keys);
 235  E :    g_crash_key_storage.crash_keys = crash_keys;
 236  E :    DCHECK_EQ(0u, g_crash_key_storage.crash_key_count);
 237  E :    g_crash_key_storage.crash_key_count = count;
 238  E :  }
 239    :  
 240    :  bool ReadCrashKeysFromProcess(HANDLE process,
 241  E :                                std::vector<CrashKey>* crash_keys) {
 242  E :    ::common::ModuleVector modules;
 243  E :    ::common::GetProcessModules(process, &modules);
 244    :  
 245  E :    CurrentModuleMatcher module_matcher;
 246    :  
 247  E :    for (const auto& module : modules) {
 248  E :      if (module_matcher.Matches(process, module))
 249  E :        return ReadCrashKeysFromProcessModule(process, module, crash_keys);
 250  E :    }
 251  i :    return false;
 252  E :  }
 253    :  
 254    :  }  // namespace internal
 255    :  }  // namespace api
 256    :  }  // namespace kasko

Coverage information generated Thu Jan 14 17:40:38 2016.