Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/conflicts/module_database_win.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 | |
| 11 namespace { | |
| 12 | |
| 13 // Document the assumptions made on the ProcessType enum in order to convert | |
| 14 // them to bits. | |
| 15 static_assert(content::PROCESS_TYPE_UNKNOWN == 1, | |
| 16 "assumes unknown process type has value 1"); | |
| 17 static_assert(content::PROCESS_TYPE_BROWSER == 2, | |
| 18 "assumes browser process type has value 2"); | |
| 19 constexpr uint32_t kMinProcessType = content::PROCESS_TYPE_BROWSER; | |
| 20 | |
| 21 } // namespace | |
| 22 | |
| 23 ModuleDatabase::ModuleDatabase( | |
| 24 scoped_refptr<base::SequencedTaskRunner> task_runner) | |
| 25 : task_runner_(std::move(task_runner)), weak_ptr_factory_(this) {} | |
| 26 | |
| 27 ModuleDatabase::~ModuleDatabase() = default; | |
| 28 | |
| 29 void ModuleDatabase::OnProcessStarted(uint32_t process_id, | |
| 30 uint64_t creation_time, | |
| 31 content::ProcessType process_type) { | |
| 32 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | |
| 33 CreateProcessInfo(process_id, creation_time, process_type); | |
| 34 } | |
| 35 | |
| 36 void ModuleDatabase::OnModuleLoad(uint32_t process_id, | |
| 37 uint64_t creation_time, | |
| 38 const base::FilePath& module_path, | |
| 39 uint32_t module_size, | |
| 40 uint32_t module_time_date_stamp, | |
| 41 uintptr_t module_load_address) { | |
| 42 // Messages can arrive from any thread (UI thread for calls over IPC, and | |
| 43 // anywhere at all for calls from ModuleWatcher), so bounce if necessary. | |
| 44 if (!task_runner_->RunsTasksOnCurrentThread()) { | |
| 45 task_runner_->PostTask( | |
| 46 FROM_HERE, base::Bind(&ModuleDatabase::OnModuleLoad, | |
| 47 weak_ptr_factory_.GetWeakPtr(), process_id, | |
| 48 creation_time, module_path, module_size, | |
| 49 module_time_date_stamp, module_load_address)); | |
| 50 return; | |
| 51 } | |
| 52 | |
| 53 // In theory this should always succeed. However, it is possible for a client | |
| 54 // to misbehave and send out-of-order messages. It is easy to be tolerant of | |
| 55 // this by simply not updating the process info in this case. It's not worth | |
| 56 // crashing if this data is slightly out of sync as this is purely | |
| 57 // informational. | |
| 58 auto* process_info = GetProcessInfo(process_id, creation_time); | |
| 59 if (!process_info) | |
| 60 return; | |
| 61 | |
| 62 auto* module_info = | |
| 63 FindOrCreateModuleInfo(module_path, module_size, module_time_date_stamp); | |
| 64 | |
| 65 // Update the list of process types that this module has been seen in. | |
| 66 module_info->process_types |= ProcessTypeToBit(process_info->process_type); | |
| 67 | |
| 68 // Update the load address maps. | |
| 69 InsertLoadAddress(module_info->module_id, module_load_address, | |
| 70 &process_info->loaded_modules); | |
| 71 RemoveLoadAddressById(module_info->module_id, | |
| 72 &process_info->unloaded_modules); | |
| 73 } | |
| 74 | |
| 75 void ModuleDatabase::OnModuleUnload(uint32_t process_id, | |
| 76 uint64_t creation_time, | |
| 77 uintptr_t module_load_address) { | |
| 78 // Messages can arrive from any thread (UI thread for calls over IPC, and | |
| 79 // anywhere at all for calls from ModuleWatcher), so bounce if necessary. | |
| 80 if (!task_runner_->RunsTasksOnCurrentThread()) { | |
| 81 task_runner_->PostTask( | |
| 82 FROM_HERE, base::Bind(&ModuleDatabase::OnModuleUnload, | |
| 83 weak_ptr_factory_.GetWeakPtr(), process_id, | |
| 84 creation_time, module_load_address)); | |
| 85 return; | |
| 86 } | |
| 87 | |
| 88 // See the long-winded comment in OnModuleLoad about reasons why this can | |
| 89 // fail (but shouldn't normally). | |
| 90 auto* process_info = GetProcessInfo(process_id, creation_time); | |
| 91 if (!process_info) | |
| 92 return; | |
| 93 | |
| 94 // Find the module corresponding to this load address. This is O(1) in the | |
| 95 // common case of removing a recently removed module, but O(n) worst case. | |
| 96 // Thankfully, unload events occur far less often and n is quite small. | |
| 97 size_t i = FindLoadAddressIndexByAddress(module_load_address, | |
| 98 process_info->loaded_modules); | |
| 99 | |
| 100 // No such module found. This shouldn't happen either, unless messages are | |
| 101 // malformed or out of order. Gracefully fail in this case. | |
| 102 if (i == kInvalidIndex) | |
| 103 return; | |
| 104 | |
| 105 ModuleId module_id = process_info->loaded_modules[i].first; | |
| 106 | |
| 107 // Remove from the loaded module list and insert into the unloaded module | |
| 108 // list. | |
| 109 RemoveLoadAddressByIndex(i, &process_info->loaded_modules); | |
| 110 InsertLoadAddress(module_id, module_load_address, | |
| 111 &process_info->unloaded_modules); | |
| 112 } | |
| 113 | |
| 114 void ModuleDatabase::OnProcessEnded(uint32_t process_id, | |
| 115 uint64_t creation_time) { | |
| 116 // Messages can arrive from any thread (UI thread for calls over IPC, and | |
| 117 // anywhere at all for calls from ModuleWatcher), so bounce if necessary. | |
| 118 if (!task_runner_->RunsTasksOnCurrentThread()) { | |
| 119 task_runner_->PostTask( | |
| 120 FROM_HERE, | |
| 121 base::Bind(&ModuleDatabase::OnProcessEnded, | |
| 122 weak_ptr_factory_.GetWeakPtr(), process_id, creation_time)); | |
| 123 return; | |
| 124 } | |
| 125 | |
| 126 DeleteProcessInfo(process_id, creation_time); | |
| 127 } | |
| 128 | |
| 129 // static | |
| 130 uint32_t ModuleDatabase::ProcessTypeToBit(content::ProcessType process_type) { | |
| 131 uint32_t bit_index = static_cast<uint32_t>(process_type) - kMinProcessType; | |
| 132 DCHECK_LE(0u, bit_index); | |
| 133 DCHECK_GE(31u, bit_index); | |
| 134 uint32_t bit = (1 << bit_index); | |
| 135 return bit; | |
| 136 } | |
| 137 | |
| 138 // static | |
| 139 content::ProcessType ModuleDatabase::BitIndexToProcessType(uint32_t bit_index) { | |
| 140 DCHECK_LE(0u, bit_index); | |
| 141 DCHECK_GE(31u, bit_index); | |
| 142 return static_cast<content::ProcessType>(bit_index + kMinProcessType); | |
| 143 } | |
| 144 | |
| 145 // static | |
| 146 size_t ModuleDatabase::FindLoadAddressIndexById( | |
| 147 ModuleId module_id, | |
| 148 const ModuleLoadAddresses& load_addresses) { | |
| 149 // Process elements in reverse order so that RemoveLoadAddressById can handle | |
| 150 // the more common case of removing the maximum element in O(1). | |
| 151 for (size_t i = load_addresses.size() - 1; i < load_addresses.size(); --i) { | |
| 152 if (load_addresses[i].first == module_id) | |
| 153 return i; | |
| 154 } | |
| 155 return kInvalidIndex; | |
| 156 } | |
| 157 | |
| 158 // static | |
| 159 size_t ModuleDatabase::FindLoadAddressIndexByAddress( | |
| 160 uintptr_t load_address, | |
| 161 const ModuleLoadAddresses& load_addresses) { | |
| 162 for (size_t i = 0; i < load_addresses.size(); ++i) { | |
| 163 if (load_addresses[i].second == load_address) | |
| 164 return i; | |
| 165 } | |
| 166 return kInvalidIndex; | |
| 167 } | |
| 168 | |
| 169 // static | |
| 170 void ModuleDatabase::InsertLoadAddress(ModuleId module_id, | |
| 171 uintptr_t load_address, | |
| 172 ModuleLoadAddresses* load_addresses) { | |
| 173 // A very small optimization: the largest module_id is always placed at the | |
| 174 // end of the array. This is the most common case, and allows O(1) | |
| 175 // determination that a |module_id| isn't present when it's bigger than the | |
| 176 // maximum already in the array. This keeps insertions to O(1) in the usual | |
| 177 // case. | |
| 178 if (load_addresses->empty() || module_id > load_addresses->back().first) { | |
| 179 load_addresses->emplace_back(module_id, load_address); | |
| 180 return; | |
| 181 } | |
| 182 | |
| 183 // If the module exists in the collection then update the load address and | |
| 184 // return. This should never really occur, unless the client is deliberately | |
| 185 // misbehaving or a race causes a reload event (at a different address) to be | |
| 186 // processed before the corresponding unload. This is very unlikely. | |
| 187 size_t i = FindLoadAddressIndexById(module_id, *load_addresses); | |
| 188 if (i != kInvalidIndex) { | |
| 189 (*load_addresses)[i].second = load_address; | |
| 190 return; | |
| 191 } | |
| 192 | |
| 193 // The module does not exist, and by definition is smaller in value than | |
| 194 // the largest module ID already present. Add it, ensuring that the largest | |
| 195 // module ID stays at the end. | |
| 196 load_addresses->emplace(--load_addresses->end(), module_id, load_address); | |
| 197 } | |
| 198 | |
| 199 // static | |
| 200 void ModuleDatabase::RemoveLoadAddressById( | |
| 201 ModuleId module_id, | |
| 202 ModuleLoadAddresses* load_addresses) { | |
| 203 if (load_addresses->empty()) | |
| 204 return; | |
| 205 | |
| 206 // This handles the special case of removing the max element in O(1), as | |
| 207 // FindLoadAddressIndexById processes the elements in reverse order. | |
| 208 size_t i = FindLoadAddressIndexById(module_id, *load_addresses); | |
| 209 RemoveLoadAddressByIndex(i, load_addresses); | |
| 210 } | |
| 211 | |
| 212 // static | |
| 213 void ModuleDatabase::RemoveLoadAddressByIndex( | |
| 214 size_t index, | |
| 215 ModuleLoadAddresses* load_addresses) { | |
| 216 DCHECK_LT(index, load_addresses->size()); | |
| 217 | |
| 218 // Special case: removing the last module (with maximum id). Need to find the | |
| 219 // new maximum element and ensure it goes to the end. | |
| 220 if (load_addresses->size() > 2 && index + 1 == load_addresses->size()) { | |
| 221 // Note that |index| == load_addresses->size() - 1, and is the last | |
| 222 // indexable element in the vector. | |
| 223 | |
| 224 // Find the index of the new maximum element. | |
| 225 ModuleId max_id = -1; // These start at zero. | |
| 226 size_t max_index = kInvalidIndex; | |
| 227 for (size_t i = 0; i < load_addresses->size() - 1; ++i) { | |
| 228 if ((*load_addresses)[i].first > max_id) { | |
| 229 max_id = (*load_addresses)[i].first; | |
| 230 max_index = i; | |
| 231 } | |
| 232 } | |
| 233 | |
| 234 // Remove the last (max) element. | |
| 235 load_addresses->resize(index); | |
| 236 | |
| 237 // If the new max element isn't in the last position, then swap it so it is. | |
| 238 size_t last_index = load_addresses->size() - 1; | |
| 239 if (max_index != last_index) | |
| 240 std::swap((*load_addresses)[max_index], (*load_addresses)[last_index]); | |
| 241 | |
| 242 return; | |
| 243 } | |
| 244 | |
| 245 // If the element to be removed is second last then a single copy is | |
| 246 // sufficient. | |
| 247 if (index + 2 == load_addresses->size()) { | |
| 248 (*load_addresses)[index] = (*load_addresses)[index + 1]; | |
| 249 } else { | |
| 250 // In the general case two copies are necessary. | |
| 251 int max_index = load_addresses->size() - 1; | |
| 252 (*load_addresses)[index] = (*load_addresses)[max_index - 1]; | |
| 253 (*load_addresses)[max_index - 1] = (*load_addresses)[max_index]; | |
| 254 } | |
| 255 | |
| 256 // Remove the last element, which is now duplicated. | |
| 257 load_addresses->resize(load_addresses->size() - 1); | |
| 258 } | |
| 259 | |
| 260 ModuleDatabase::ModuleInfo* ModuleDatabase::FindOrCreateModuleInfo( | |
| 261 const base::FilePath& module_path, | |
| 262 uint32_t module_size, | |
| 263 uint32_t module_time_date_stamp) { | |
| 264 auto result = modules_.emplace(ModuleInfo( | |
|
grt (UTC plus 2)
2017/01/04 08:41:42
use emplace to its fullest potential and avoid con
chrisha
2017/01/04 16:28:37
Mind blown. Thanks.
grt (UTC plus 2)
2017/01/04 21:27:11
perfect forwarding FTW!
| |
| 265 module_path, module_size, module_time_date_stamp, modules_.size())); | |
| 266 // Cast away constness so that the non-key portions of the object can be | |
| 267 // modified. The key portions of the object are themselves marked const, so | |
| 268 // this causes no trouble with std::set. | |
| 269 return const_cast<ModuleInfo*>(&(*result.first)); | |
| 270 } | |
| 271 | |
| 272 ModuleDatabase::ProcessInfo* ModuleDatabase::GetProcessInfo( | |
| 273 uint32_t process_id, | |
| 274 uint64_t creation_time) { | |
| 275 ProcessInfo key(process_id, creation_time, content::PROCESS_TYPE_UNKNOWN); | |
| 276 auto it = processes_.find(key); | |
| 277 if (it == processes_.end()) | |
| 278 return nullptr; | |
| 279 // Cast away constness so that the non-key portions of the object can be | |
| 280 // modified. The key portions of the object are themselves marked const, so | |
| 281 // this causes no trouble with std::set. | |
| 282 return const_cast<ProcessInfo*>(&(*it)); | |
| 283 } | |
| 284 | |
| 285 void ModuleDatabase::CreateProcessInfo(uint32_t process_id, | |
| 286 uint64_t creation_time, | |
| 287 content::ProcessType process_type) { | |
| 288 processes_.emplace(ProcessInfo(process_id, creation_time, process_type)); | |
|
grt (UTC plus 2)
2017/01/04 08:41:42
processes_.emplace(process_id, creation_time, proc
chrisha
2017/01/04 16:28:37
Done.
| |
| 289 } | |
| 290 | |
| 291 void ModuleDatabase::DeleteProcessInfo(uint32_t process_id, | |
| 292 uint64_t creation_time) { | |
| 293 ProcessInfo key(process_id, creation_time, content::PROCESS_TYPE_UNKNOWN); | |
| 294 auto it = processes_.find(key); | |
| 295 if (it != processes_.end()) | |
| 296 processes_.erase(it); | |
| 297 } | |
| 298 | |
| 299 // ModuleDatabase::ModuleInfo -------------------------------------------------- | |
| 300 | |
| 301 ModuleDatabase::ModuleInfo::ModuleInfo(const base::FilePath& module_path, | |
| 302 uint32_t module_size, | |
| 303 uint32_t module_time_date_stamp, | |
| 304 uint32_t module_id) | |
| 305 : module_path(module_path), | |
| 306 module_size(module_size), | |
| 307 module_time_date_stamp(module_time_date_stamp), | |
| 308 module_id(module_id), | |
| 309 process_types(0) {} | |
| 310 | |
| 311 bool ModuleDatabase::ModuleInfo::operator<(const ModuleInfo& mi) const { | |
| 312 // The key consists of the triplet of | |
| 313 // (module_path, module_size, module_time_date_stamp). | |
| 314 // Use the std::tuple lexicographic comparison operator. | |
| 315 return std::make_tuple(module_path, module_size, module_time_date_stamp) < | |
|
grt (UTC plus 2)
2017/01/04 08:41:42
#include <tuple>
chrisha
2017/01/04 16:28:37
Done.
| |
| 316 std::make_tuple(mi.module_path, mi.module_size, | |
| 317 mi.module_time_date_stamp); | |
| 318 } | |
| 319 | |
| 320 // ModuleDatabase::ProcessInfo ------------------------------------------------- | |
| 321 | |
| 322 ModuleDatabase::ProcessInfo::ProcessInfo(uint32_t process_id, | |
| 323 uint64_t creation_time, | |
| 324 content::ProcessType process_type) | |
| 325 : process_id(process_id), | |
| 326 creation_time(creation_time), | |
| 327 process_type(process_type) {} | |
| 328 | |
| 329 bool ModuleDatabase::ProcessInfo::operator<(const ProcessInfo& pi) const { | |
| 330 // The key consists of the pair of (process_id, creation_time). | |
| 331 // Use the std::tuple lexicographic comparison operator. | |
| 332 return std::make_tuple(process_id, creation_time) < | |
| 333 std::make_tuple(pi.process_id, pi.creation_time); | |
| 334 } | |
| OLD | NEW |