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

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
94.7%1441520.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/registry_cache.h"
  16    :  
  17    :  #include <map>
  18    :  
  19    :  #include "base/base_paths.h"
  20    :  #include "base/file_version_info_win.h"
  21    :  #include "base/path_service.h"
  22    :  #include "base/memory/scoped_ptr.h"
  23    :  #include "base/strings/string_number_conversions.h"
  24    :  #include "base/strings/stringprintf.h"
  25    :  #include "syzygy/pe/metadata.h"
  26    :  
  27    :  namespace agent {
  28    :  namespace asan {
  29    :  
  30    :  const size_t RegistryCache::kDefaultMaxDaysInRegistry = 360;
  31    :  const size_t RegistryCache::kDefaultMaxEntriesPerVersion = 100;
  32    :  const size_t RegistryCache::kDefaultMaxModules = 50;
  33    :  const size_t RegistryCache::kDefaultMaxVersions = 5;
  34    :  const HKEY RegistryCache::kRegistryRootKey = HKEY_CURRENT_USER;
  35    :  const wchar_t RegistryCache::kRegistryBaseKey[] =
  36    :      L"Software\\Google\\Syzygy\\RegistryCache\\";
  37    :  
  38    :  RegistryCache::RegistryCache(const wchar_t* registry_name)
  39    :      : max_days_in_registry_(kDefaultMaxDaysInRegistry),
  40    :        max_entries_per_version_(kDefaultMaxEntriesPerVersion),
  41    :        max_modules_(kDefaultMaxModules),
  42    :        max_versions_(kDefaultMaxVersions),
  43    :        registry_cache_key_(kRegistryBaseKey),
  44  E :        is_init_(false) {
  45  E :    registry_cache_key_.append(registry_name);
  46  E :  }
  47    :  
  48    :  RegistryCache::RegistryCache(const wchar_t* registry_name,
  49    :                               size_t max_days_in_registry,
  50    :                               size_t max_entries_per_version,
  51    :                               size_t max_modules,
  52    :                               size_t max_versions)
  53    :      : max_days_in_registry_(max_days_in_registry),
  54    :        max_entries_per_version_(max_entries_per_version),
  55    :        max_modules_(max_modules),
  56    :        max_versions_(max_versions),
  57    :        registry_cache_key_(kRegistryBaseKey),
  58  E :        is_init_(false) {
  59  E :    registry_cache_key_.append(registry_name);
  60  E :  }
  61    :  
  62  E :  bool RegistryCache::Init() {
  63    :    // Always start by cleaning up the values, to limit the size of entries in
  64    :    // the registry.
  65  E :    CleanUp();
  66    :    // We can fail if we are not able to initialize the module information.
  67  E :    is_init_ = InitModuleInfo();
  68  E :    if (!is_init_)
  69  i :      return false;
  70  E :    LoadEntries();
  71  E :    return true;
  72  E :  }
  73    :  
  74  E :  void RegistryCache::AddOrUpdateStackId(StackId stack_id) {
  75  E :    DCHECK(is_init_);
  76    :  
  77    :    base::win::RegKey module_key(kRegistryRootKey, module_key_name_.c_str(),
  78  E :        KEY_ALL_ACCESS);
  79  E :    for (RegValueIter iter(kRegistryRootKey, module_key_name_.c_str());
  80  E :      iter.Valid(); ++iter) {
  81  E :      DCHECK_EQ(sizeof(StackId), iter.ValueSize());
  82  E :      const StackId* ptr_value = reinterpret_cast<const StackId*>(iter.Value());
  83    :      // We don't break out of the loop, just in case there are redundant values
  84    :      // (shouldn't normally occur).
  85  E :      if (*ptr_value == stack_id)
  86  E :        module_key.DeleteValue(iter.Name());
  87  E :    }
  88    :    std::wstring name =
  89  E :        base::Int64ToString16(base::Time::Now().ToInternalValue());
  90  E :    module_key.WriteValue(name.c_str(), &stack_id, sizeof(stack_id), REG_BINARY);
  91  E :    entries_.insert(stack_id);
  92  E :  }
  93    :  
  94    :  bool RegistryCache::DoesIdExist(
  95  E :      common::StackCapture::StackId allocation_stack_id) const {
  96  E :    DCHECK(is_init_);
  97    :  
  98  E :    return entries_.find(allocation_stack_id) != entries_.end();
  99  E :  }
 100    :  
 101    :  bool RegistryCache::RemoveStackId(
 102  E :      common::StackCapture::StackId allocation_stack_id) {
 103  E :    DCHECK(is_init_);
 104    :  
 105  E :    return entries_.erase(allocation_stack_id) != 0;
 106  E :  }
 107    :  
 108    :  // static
 109  E :  void RegistryCache::DeleteRegistryTree(const wchar_t* registry_name) {
 110    :    base::win::RegKey base_key(
 111  E :        kRegistryRootKey, kRegistryBaseKey, KEY_ALL_ACCESS);
 112  E :    base_key.DeleteKey(registry_name);
 113  E :  }
 114    :  
 115  E :  bool RegistryCache::InitModuleInfo() {
 116  E :    base::FilePath file_path;
 117  E :    if (PathService::Get(base::FILE_MODULE, &file_path)) {
 118  E :      module_name_ = file_path.BaseName().value();
 119  E :    } else {
 120  i :      LOG(ERROR) << "Cannot get the module name.";
 121  i :      return false;
 122    :    }
 123    :  
 124  E :    if (module_name_.empty()) {
 125  i :      LOG(ERROR) << "Module name is empty.";
 126  i :      return false;
 127    :    }
 128    :  
 129    :    // Get the module version. We start by grabbing the product version from the
 130    :    // file version information.
 131  E :    module_version_.clear();
 132    :    scoped_ptr<FileVersionInfo> version_info(
 133  E :        FileVersionInfo::CreateFileVersionInfo(file_path));
 134  E :    if (version_info)
 135  E :      module_version_ = version_info->product_version();
 136  E :    if (module_version_.empty()) {
 137    :      // If that fails, we try grabbing the version from the PE signature.
 138  E :      pe::PEFile pe_file;
 139  E :      if (pe_file.Init(file_path)) {
 140  E :        pe::PEFile::Signature signature;
 141  E :        pe_file.GetSignature(&signature);
 142    :        module_version_ = base::StringPrintf(
 143  E :            L"%08X%x", signature.module_time_date_stamp, signature.module_size);
 144  E :      } else {
 145    :        // If all fails, we bail.
 146  i :        LOG(ERROR) << "Cannot get the module version.";
 147  i :        return false;
 148    :      }
 149  E :    }
 150  E :    module_key_name_ = registry_cache_key_;
 151  E :    module_key_name_.append(1, L'\\').append(module_name_);
 152  E :    module_key_name_.append(1, L'\\').append(module_version_);
 153    :  
 154  E :    return true;
 155  E :  }
 156    :  
 157  E :  void RegistryCache::CleanUp() {
 158    :    // Cleanup each top-level key (ie. module level).
 159  E :    std::wstring key_name;
 160    :    base::win::RegKey base_key(kRegistryRootKey, registry_cache_key_.c_str(),
 161  E :                               KEY_ALL_ACCESS);
 162  E :    base::Time newest_value;
 163  E :    std::multimap<base::Time, std::wstring> values;
 164  E :    for (RegKeyIter iter(kRegistryRootKey, registry_cache_key_.c_str());
 165  E :         iter.Valid(); ++iter) {
 166  E :      key_name = registry_cache_key_;
 167  E :      key_name.append(1, L'\\').append(iter.Name());
 168  E :      CleanUpModule(key_name, &newest_value);
 169    :      // Delete key if empty, otherwise memorize it for possible deletion later.
 170  E :      RegKeyIter iter2(kRegistryRootKey, key_name.c_str());
 171  E :      if (iter2.SubkeyCount() <= 0) {
 172  E :        base_key.DeleteKey(iter.Name());
 173  E :      } else {
 174  E :        values.insert(std::make_pair(newest_value, iter.Name()));
 175    :      }
 176  E :    }
 177    :  
 178    :    // Delete oldest entries until we satisfy the maximum number of versions in
 179    :    // the module.
 180  E :    while (values.size() > max_modules_) {
 181  E :      base_key.DeleteKey(values.begin()->second.c_str());
 182  E :      values.erase(values.begin());
 183  E :    }
 184  E :  }
 185    :  
 186    :  void RegistryCache::CleanUpModule(const std::wstring& base_key_name,
 187  E :                                    base::Time* newest) {
 188  E :    std::wstring key_name;
 189  E :    base::win::RegKey key, base_key;
 190  E :    base::Time newest_value;
 191  E :    std::multimap<base::Time, std::wstring> values;
 192    :  
 193  E :    base_key.Open(kRegistryRootKey, base_key_name.c_str(), KEY_ALL_ACCESS);
 194    :    // Go through each key (ie. version level) and cleanup the values.
 195  E :    RegKeyIter iter(kRegistryRootKey, base_key_name.c_str());
 196  E :    for (; iter.Valid(); ++iter) {
 197  E :      key_name = base_key_name;
 198  E :      key_name.append(1, L'\\').append(iter.Name());
 199  E :      key.Open(kRegistryRootKey, key_name.c_str(), KEY_ALL_ACCESS);
 200  E :      CleanUpVersion(&key, &newest_value);
 201    :      // Delete key if empty, otherwise memorize it for possible deletion later.
 202  E :      if (key.GetValueCount() == 0) {
 203  E :        base_key.DeleteKey(iter.Name());
 204  E :      } else {
 205  E :        values.insert(std::make_pair(newest_value, iter.Name()));
 206    :      }
 207  E :      key.Close();
 208  E :    }
 209    :  
 210    :    // Delete oldest entries until we satisfy the maximum number of versions in
 211    :    // the module.
 212  E :    while (values.size() > max_versions_) {
 213  E :      base_key.DeleteKey(values.begin()->second.c_str());
 214  E :      values.erase(values.begin());
 215  E :    }
 216    :  
 217    :    // Set |newest| to the timestamp of the newest version of the current module.
 218  E :    if (newest != nullptr) {
 219  E :      if (!values.empty()) {
 220  E :        *newest = values.rbegin()->first;
 221  E :      } else {
 222  E :        *newest = base::Time::UnixEpoch();
 223    :      }
 224    :    }
 225  E :  }
 226    :  
 227    :  void RegistryCache::CleanUpVersion(base::win::RegKey* base_key,
 228  E :                                     base::Time* newest) {
 229  E :    DCHECK_NE(static_cast<base::win::RegKey*>(nullptr), base_key);
 230    :  
 231  E :    std::multimap<base::Time, std::wstring> values;
 232    :  
 233    :    // Iterate over the values, get the time (corresponds to the name) and store
 234    :    // in a map for potential deletion.
 235  E :    for (RegValueIter iter(base_key->Handle(), L""); iter.Valid(); ++iter) {
 236    :      int64 TimeInternalValue;
 237    :      // If the time is not valid or if the value size is wrong, set its time to
 238    :      // a really old one to force its deletion.
 239    :      if (base::StringToInt64(iter.Name(), &TimeInternalValue) &&
 240  E :          iter.ValueSize() == sizeof(StackId)) {
 241    :        values.insert(std::make_pair(
 242  E :            base::Time::FromInternalValue(TimeInternalValue), iter.Name()));
 243  E :      } else {
 244  i :        values.insert(std::make_pair(base::Time::UnixEpoch(), iter.Name()));
 245  E :      }
 246  E :    }
 247    :  
 248  E :    int nb_remaining_entries = max_entries_per_version_;
 249    :    // Iterate over the map and, for each entry, verify if it needs to be purged.
 250    :    // An entry is kept if its age is smaller than |kMaxDaysInRegistry| and if we
 251    :    // have not reached |kMaxEntriesPerVersion| entries. Since the entries are
 252    :    // sorted by age, we ensure that the kept entries are always the most recent.
 253  E :    for (auto iter(values.rbegin()); iter != values.rend(); ++iter) {
 254  E :      if (nb_remaining_entries > 0) {
 255  E :        base::TimeDelta delta(base::Time::Now() - (iter->first));
 256  E :        if (delta.InDays() < max_days_in_registry_) {
 257  E :          nb_remaining_entries--;
 258  E :          continue;
 259    :        }
 260    :        // Once we find an entry that's too old, delete all the following as well.
 261  E :        nb_remaining_entries = 0;
 262    :      }
 263  E :      base_key->DeleteValue(iter->second.c_str());
 264  E :    }
 265    :  
 266    :    // Set |newest| to the timestamp of the newest entry of the current version.
 267  E :    if (newest != nullptr) {
 268  E :      if (!values.empty()) {
 269  E :        *newest = values.rbegin()->first;
 270  E :      } else {
 271  E :        *newest = base::Time::UnixEpoch();
 272    :      }
 273    :    }
 274  E :  }
 275    :  
 276  E :  void RegistryCache::LoadEntries() {
 277    :    // Load the entries from the registry into the container.
 278  E :    for (RegValueIter iter(kRegistryRootKey, module_key_name_.c_str());
 279  E :         iter.Valid(); ++iter) {
 280  E :      DCHECK_EQ(sizeof(StackId), iter.ValueSize());
 281  E :      const StackId* ptr_value = reinterpret_cast<const StackId*>(iter.Value());
 282  E :      entries_.insert(*ptr_value);
 283  E :    }
 284  E :  }
 285    :  
 286    :  }  // namespace asan
 287    :  }  // namespace agent

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