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
|