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 : // This class allows us to save, in the Windows registry, the relative IDs of
16 : // allocation stack traces for the current module & version. Module refers to
17 : // the filename of the module containing the code (usually an executable or a
18 : // DLL). The version is determined by various methods (see implementation of
19 : // InitModuleInfo). Note that there is no standard naming convention for the
20 : // version as it's used as is.
21 : //
22 : // Values are stored in a base key that depends on the name provided to the
23 : // constructor. Each module gets its own registry key under the base key and
24 : // that key's name is the same as the module base name. Inside each module key
25 : // is a second level of keys, corresponding to the versions (same name as the
26 : // version). An example of the key hierarchy is presented in the following
27 : // diagram:
28 : //
29 : // Base key +---> chrome.exe +---> 39.0.2171.95
30 : // | +---> 39.0.2171.99
31 : // |
32 : // +---> program.dll +---> Version 1
33 : // |
34 : // +---> program.exe +---> 1
35 : // +---> 2
36 : // +---> 3
37 : //
38 : // Finally, inside each version key are the entries. Each entry corresponds to
39 : // an allocation stack trace. The name of the entry corresponds to its timestamp
40 : // (return value of ToInternalValue) and its value corresponds to the stack ID.
41 : //
42 : // At every initialization, the entries of all modules/versions are purged
43 : // (regardless of the current module/version). This is done by removing all
44 : // entries older than |kMaxDaysInRegistry| as well as limiting the total number
45 : // of entries inside each version to |kMaxEntriesPerVersion| and by deleting
46 : // empty module and version keys.
47 :
48 : #ifndef SYZYGY_AGENT_ASAN_REGISTRY_CACHE_H_
49 : #define SYZYGY_AGENT_ASAN_REGISTRY_CACHE_H_
50 :
51 : #include <unordered_set>
52 :
53 : #include "base/macros.h"
54 : #include "base/files/file_path.h"
55 : #include "base/strings/string_number_conversions.h"
56 : #include "base/time/time.h"
57 : #include "base/win/registry.h"
58 : #include "syzygy/agent/asan/heap_checker.h"
59 : #include "syzygy/agent/common/stack_capture.h"
60 :
61 m : namespace agent {
62 m : namespace asan {
63 :
64 m : class RegistryCache {
65 m : public:
66 : // Default value for |max_days_in_registry_|.
67 m : static const size_t kDefaultMaxDaysInRegistry;
68 :
69 : // Default value for |max_entries_per_version_|.
70 m : static const size_t kDefaultMaxEntriesPerVersion;
71 :
72 : // Default value for |max_modules_|.
73 m : static const size_t kDefaultMaxModules;
74 :
75 : // Default value for |max_versions_|.
76 m : static const size_t kDefaultMaxVersions;
77 :
78 : // Root of the path in the registry (values that make sense are HCU and HLM).
79 m : static const HKEY kRegistryRootKey;
80 :
81 : // The base path that will contain the module keys (under |kRoot|). This gets
82 : // completed with the registry name that gets passed to the constructor to
83 : // form |registry_key_|.
84 m : static const wchar_t kRegistryBaseKey[];
85 : // Constructors.
86 : // @param registry_name The base name that is used for storing in the
87 : // registry.
88 : // @param max_days_in_registry Value of max_days_in_registry_.
89 : // @param max_entries_per_version Value of max_entries_per_version_.
90 : // @param max_modules Value of max_modules_.
91 : // @param max_versions Value of max_versions_.
92 m : explicit RegistryCache(const wchar_t* registry_name);
93 m : RegistryCache(const wchar_t* registry_name,
94 m : size_t max_days_in_registry,
95 m : size_t max_entries_per_version,
96 m : size_t max_modules,
97 m : size_t max_versions);
98 :
99 : // Initializes the registry cache and prunes old values in the registry. This
100 : // must be called once, before any other method. Note that this function is
101 : // not thread-safe.
102 : // @returns false if an error occured.
103 m : bool Init();
104 :
105 : // Adds a new |allocation_stack_id|, if it was not existent. Otherwise,
106 : // updates it by removing corresponding registry value and inserting a new
107 : // one. note that the ID must be a relative one. Will not do anything if the
108 : // module has not been initialized properly.
109 : // @pre is_init_ is true.
110 : // @param allocation_stack_id The relative stack ID to add.
111 m : void AddOrUpdateStackId(common::StackCapture::StackId allocation_stack_id);
112 :
113 : // Checks if |allocation_stack_id| has been loaded from the registry. Will
114 : // always return false if the module has not been initialized properly.
115 : // @pre is_init_ is true.
116 : // @param allocation_stack_id The relative stack ID to lookup.
117 : // @returns true if the ID has been found, false otherwise or if not
118 : // initialized.
119 m : bool DoesIdExist(common::StackCapture::StackId allocation_stack_id) const;
120 :
121 : // Removes|allocation_stack_id| from the registry. Will always return false
122 : // if the module has not been initialized properly.
123 : // @pre is_init_ is true.
124 : // @param allocation_stack_id The relative stack ID to remove.
125 : // @returns true if the ID has been found, false otherwise or if not
126 : // initialized.
127 m : bool RemoveStackId(common::StackCapture::StackId allocation_stack_id);
128 :
129 : // Deletes the registry key corresponding to |registry_name|, including
130 : // everything below it. Use carefully!
131 : // @param registry_name The base name whose key will be deleted from the
132 : // registry.
133 m : static void DeleteRegistryTree(const wchar_t* registry_name);
134 :
135 m : protected:
136 : // For convenience (also used in unittests).
137 m : typedef base::win::RegistryValueIterator RegValueIter;
138 m : typedef base::win::RegistryKeyIterator RegKeyIter;
139 m : typedef common::StackCapture::StackId StackId;
140 :
141 : // Maximum age allowed for an entry (in days). Any entry older than this value
142 : // will be purged during cleaning.
143 m : size_t max_days_in_registry_;
144 :
145 : // Maximum number of entries allowed per module version. The cleaning process
146 : // will ensure that the number of entries in a module version does not exceed
147 : // this threshold by purging the oldest ones.
148 m : size_t max_entries_per_version_;
149 :
150 : // Maximum number of modules allowed. The cleaning process will ensure that
151 : // the number of modules does not exceed this threshold by purging the oldest
152 : // ones.
153 m : size_t max_modules_;
154 :
155 : // Maximum number of versions allowed per module. The cleaning process will
156 : // ensure that the number of versions for a module does not exceed this
157 : // threshold by purging the oldest ones.
158 m : size_t max_versions_;
159 :
160 : // The base path that will contain the module keys (under |kRoot|).
161 m : std::wstring registry_cache_key_;
162 :
163 : // Contains the name of the module.
164 m : base::FilePath::StringType module_name_;
165 : // Contains the module version.
166 m : std::wstring module_version_;
167 : // Contains the path of the module key in the registry.
168 m : std::wstring module_key_name_;
169 :
170 m : private:
171 : // Initializes the module name and version. This can fail if we are not able
172 : // to succesfully identify both.
173 : // @returns false if an error occured.
174 m : bool InitModuleInfo();
175 :
176 : // Function that starts the cleanup of old entries in the registry by going
177 : // through each top-level key (corresponding to a module name) and calling
178 : // CleanUpModule on each entry. It will also delete module keys that become
179 : // empty after the operation. Finally, it limits the number of modules to
180 : // |kMaxModules| by deleting the oldest ones, if necessary.
181 m : void CleanUp();
182 :
183 : // Function that cleans up a module key by going through each of its version
184 : // keys and calling CleanUpVersion on each entry. It will also delete version
185 : // keys that become empty after the operation. Finally, it limits the number
186 : // of versions to |kMaxVersions| by deleting the oldest ones, if necessary.
187 : // @param base_key The path of the registry key to clean up.
188 : // @param newest If not NULL, will be set to the timestamp of the newest
189 : // entry. If no entries are valid/existent, will be set to UnixEpoch.
190 m : void CleanUpModule(const std::wstring& base_key, base::Time* newest);
191 :
192 : // Purges old values and limits the total number of entries in a version key
193 : // to |kMaxEntriesPerVersion|.
194 : // @param src The registry key to clean up.
195 : // @param newest If not NULL, will be set to the timestamp of the newest
196 : // entry. If no entries are valid/existent, will be set to UnixEpoch.
197 m : void CleanUpVersion(base::win::RegKey* src, base::Time* newest);
198 :
199 : // Loads the entries from the registry for the current module.
200 m : void LoadEntries();
201 :
202 : // True if Init() has been called successfully.
203 m : bool is_init_;
204 :
205 : // The relative stack IDs that are loaded from the registry.
206 m : std::unordered_set<common::StackCapture::StackId> entries_;
207 :
208 m : DISALLOW_COPY_AND_ASSIGN(RegistryCache);
209 m : };
210 :
211 m : } // namespace asan
212 m : } // namespace agent
213 :
214 : #endif // SYZYGY_AGENT_ASAN_REGISTRY_CACHE_H_
|