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 "base/bind.h" | |
| 8 #include "base/debug/leak_annotations.h" | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
unused?
chrisha
2016/12/20 19:46:22
Done.
| |
| 9 #include "base/lazy_instance.h" | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
unused
chrisha
2016/12/20 19:46:23
Done.
| |
| 10 #include "base/strings/utf_string_conversions.h" | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
unused
chrisha
2016/12/20 19:46:23
Done.
(I really wish we had automated analysis fo
grt (UTC plus 2)
2016/12/20 21:09:52
Amen to that, brother.
| |
| 11 | |
| 12 namespace { | |
| 13 | |
| 14 // Document the assumptions made on the ProcessType enum in order to convert | |
| 15 // them to bits. | |
| 16 static_assert(1 == content::PROCESS_TYPE_UNKNOWN, | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
reverse these!
chrisha
2016/12/20 19:46:22
Done.
| |
| 17 "assumes unknown process type has value 1"); | |
| 18 static_assert(2 == content::PROCESS_TYPE_BROWSER, | |
| 19 "assumes browser process type has value 2"); | |
| 20 static constexpr uint32_t kMinProcessType = content::PROCESS_TYPE_BROWSER; | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
omit "static" in an unnamed namespace
chrisha
2016/12/20 19:46:23
Done.
| |
| 21 | |
| 22 } // namespace | |
| 23 | |
| 24 ModuleDatabase::ModuleDatabase( | |
| 25 scoped_refptr<base::SequencedTaskRunner> task_runner) | |
| 26 : task_runner_(task_runner), weak_ptr_factory_(this) {} | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
std::move(task_runner)?
chrisha
2016/12/20 19:46:23
Done.
| |
| 27 | |
| 28 ModuleDatabase::~ModuleDatabase() = default; | |
| 29 | |
| 30 void ModuleDatabase::OnProcessStarted(uint32_t process_id, | |
| 31 content::ProcessType process_type) { | |
| 32 DCHECK(task_runner_->RunsTasksOnCurrentThread()); | |
| 33 | |
| 34 auto* process_info = CreateProcessInfo(process_id, process_type); | |
| 35 // If each client is sending messages in the appropriate order then this | |
| 36 // should always be true. | |
| 37 DCHECK(process_info); | |
| 38 } | |
| 39 | |
| 40 void ModuleDatabase::OnModuleEvent(uint32_t process_id, | |
| 41 const ModuleWatcher::ModuleEvent& event) { | |
| 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(FROM_HERE, base::Bind(&ModuleDatabase::OnModuleEvent, | |
| 46 weak_ptr_factory_.GetWeakPtr(), | |
| 47 process_id, event)); | |
| 48 return; | |
| 49 } | |
| 50 | |
| 51 // In theory this should always succeed. However, it is possible for a client | |
| 52 // to misbehave and sent out-of-order messages. It is easy to be tolerant of | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
sent -> send
chrisha
2016/12/20 19:46:23
Done.
| |
| 53 // this by simply not updating the process info in this case. It's not worth | |
| 54 // crashing if this data is slightly out of sync as this is purely | |
| 55 // informational. | |
| 56 auto* process_info = GetProcessInfo(process_id); | |
| 57 DCHECK(process_info); | |
| 58 if (!process_info) | |
| 59 return; | |
| 60 | |
| 61 auto* module_info = FindOrCreateModuleInfo(event.module_path); | |
| 62 | |
| 63 // Update the list of process types that this module has been seen in. | |
| 64 module_info->process_types |= ProcessTypeToBit(process_info->process_type); | |
| 65 | |
| 66 uintptr_t load_address = | |
| 67 reinterpret_cast<uintptr_t>(event.module_load_address); | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
wdyt of making module_load_address a uintptr_t thr
chrisha
2016/12/20 19:46:23
There's one boundary I can't get rid of: the IPC m
grt (UTC plus 2)
2016/12/20 21:09:52
Naah. Thanks for clarifying the reasoning.
| |
| 68 | |
| 69 // Update the module lists for this process. | |
| 70 switch (event.event_type) { | |
| 71 case mojom::ModuleEventType::MODULE_ALREADY_LOADED: | |
| 72 case mojom::ModuleEventType::MODULE_LOADED: | |
| 73 InsertLoadAddress(module_info->module_id, load_address, | |
| 74 &process_info->loaded_modules); | |
| 75 RemoveLoadAddressById(module_info->module_id, | |
| 76 &process_info->unloaded_modules); | |
| 77 break; | |
| 78 case mojom::ModuleEventType::MODULE_UNLOADED: | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
i find it a bit confusing that unloads are handled
chrisha
2016/12/20 19:46:23
OnModuleUnload is expensive, costing an O(n) searc
grt (UTC plus 2)
2016/12/20 21:09:52
Can you clarify in comments which is trusted and w
chrisha
2016/12/21 20:14:59
I've reworked the API so there's only one way to l
| |
| 79 RemoveLoadAddressById(module_info->module_id, | |
| 80 &process_info->loaded_modules); | |
| 81 InsertLoadAddress(module_info->module_id, load_address, | |
| 82 &process_info->unloaded_modules); | |
| 83 break; | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 void ModuleDatabase::OnModuleUnload(uint32_t process_id, | |
| 88 uintptr_t load_address) { | |
| 89 // Messages can arrive from any thread (UI thread for calls over IPC, and | |
| 90 // anywhere at all for calls from ModuleWatcher), so bounce if necessary. | |
| 91 if (!task_runner_->RunsTasksOnCurrentThread()) { | |
| 92 task_runner_->PostTask( | |
| 93 FROM_HERE, | |
| 94 base::Bind(&ModuleDatabase::OnModuleUnload, | |
| 95 weak_ptr_factory_.GetWeakPtr(), process_id, load_address)); | |
| 96 return; | |
| 97 } | |
| 98 | |
| 99 // See the long-winded comment in OnModuleEvent about reasons why this can | |
| 100 // fail (but shouldn't normally). | |
| 101 auto* process_info = GetProcessInfo(process_id); | |
| 102 DCHECK(process_info); | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
remove this one, too?
chrisha
2016/12/20 19:46:23
Oops, should have been deleted.
Done.
| |
| 103 if (!process_info) | |
| 104 return; | |
| 105 | |
| 106 // Find the module corresponding to this load address. | |
| 107 int i = | |
| 108 FindLoadAddressIndexByAddress(load_address, process_info->loaded_modules); | |
| 109 | |
| 110 // No such module found. This shouldn't happen either, unless messages are | |
| 111 // malformed or out of order. Gracefully fail in this case. | |
| 112 if (i == kInvalidIndex) | |
| 113 return; | |
| 114 | |
| 115 ModuleId module_id = process_info->loaded_modules[i].first; | |
| 116 | |
| 117 // Remove from the loaded module list and insert into the unloaded module | |
| 118 // list. | |
| 119 RemoveLoadAddressByIndex(i, &process_info->loaded_modules); | |
| 120 InsertLoadAddress(module_id, load_address, &process_info->unloaded_modules); | |
| 121 } | |
| 122 | |
| 123 void ModuleDatabase::OnProcessEnded(uint32_t process_id) { | |
| 124 // Messages can arrive from any thread (UI thread for calls over IPC, and | |
| 125 // anywhere at all for calls from ModuleWatcher), so bounce if necessary. | |
| 126 if (!task_runner_->RunsTasksOnCurrentThread()) { | |
| 127 task_runner_->PostTask( | |
| 128 FROM_HERE, base::Bind(&ModuleDatabase::OnProcessEnded, | |
| 129 weak_ptr_factory_.GetWeakPtr(), process_id)); | |
| 130 return; | |
| 131 } | |
| 132 | |
| 133 bool deleted = DeleteProcessInfo(process_id); | |
| 134 // If each client is sending messages in the appropriate order then this | |
| 135 // should always be true. | |
| 136 DCHECK(deleted); | |
| 137 } | |
| 138 | |
| 139 // static | |
| 140 uint32_t ModuleDatabase::ProcessTypeToBit(content::ProcessType process_type) { | |
| 141 uint32_t bit_index = static_cast<uint32_t>(process_type) - kMinProcessType; | |
| 142 DCHECK_LE(0u, bit_index); | |
| 143 DCHECK_GE(31u, bit_index); | |
| 144 uint32_t bit = (1 << bit_index); | |
| 145 return bit; | |
| 146 } | |
| 147 | |
| 148 // static | |
| 149 content::ProcessType ModuleDatabase::BitIndexToProcessType(uint32_t bit_index) { | |
| 150 DCHECK_LE(0u, bit_index); | |
| 151 DCHECK_GE(31u, bit_index); | |
| 152 return static_cast<content::ProcessType>(bit_index + kMinProcessType); | |
| 153 } | |
| 154 | |
| 155 // static | |
| 156 int ModuleDatabase::FindLoadAddressIndexById( | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
looks like using size_t for the index would be muc
chrisha
2016/12/20 19:46:23
Indeed it would be. Done.
| |
| 157 ModuleId module_id, | |
| 158 const ModuleLoadAddresses& load_addresses) { | |
| 159 for (size_t i = 0; i < load_addresses.size(); ++i) { | |
| 160 if (load_addresses[i].first == module_id) | |
| 161 return static_cast<int>(i); | |
| 162 } | |
| 163 return kInvalidIndex; | |
| 164 } | |
| 165 | |
| 166 // static | |
| 167 int ModuleDatabase::FindLoadAddressIndexByAddress( | |
| 168 uintptr_t load_address, | |
| 169 const ModuleLoadAddresses& load_addresses) { | |
| 170 for (size_t i = 0; i < load_addresses.size(); ++i) { | |
| 171 if (load_addresses[i].second == load_address) | |
| 172 return static_cast<int>(i); | |
| 173 } | |
| 174 return kInvalidIndex; | |
| 175 } | |
| 176 | |
| 177 // static | |
| 178 void ModuleDatabase::InsertLoadAddress(ModuleId module_id, | |
|
chrisha
2016/12/19 20:15:37
grt: I was bored, so went a little overboard here
| |
| 179 uintptr_t load_address, | |
| 180 ModuleLoadAddresses* load_addresses) { | |
| 181 // A very small optimization: the largest module_id is always placed at the | |
| 182 // end of the array. This is the most common case, and allows O(1) | |
| 183 // determination that a |module_id| isn't present when it's bigger than the | |
| 184 // maximum already in the array. This keeps insertions to O(1) in the usual | |
| 185 // case. | |
| 186 if (load_addresses->size() == 0 || module_id > load_addresses->back().first) { | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
load_addresses->size() == 0 -> load_addresses->emp
chrisha
2016/12/20 19:46:23
Done.
| |
| 187 load_addresses->push_back(std::make_pair(module_id, load_address)); | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
load_addresses->emplace_back(module_id, load_addre
chrisha
2016/12/20 19:46:23
Holy jebus batman, I've never seen "emplace". TIL.
grt (UTC plus 2)
2016/12/20 21:09:52
emplace was the toy I got on my birthday from the
| |
| 188 return; | |
| 189 } | |
| 190 | |
| 191 // If the module exists in the collection then update the load address and | |
| 192 // return. | |
| 193 int i = FindLoadAddressIndexById(module_id, *load_addresses); | |
| 194 if (i != kInvalidIndex) { | |
| 195 load_addresses->at(i).second = load_address; | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
curious: why would a module's load address change?
chrisha
2016/12/20 19:46:23
A vain attempt at maintaining long term consistenc
grt (UTC plus 2)
2016/12/20 21:09:52
If unload/reload could potentially be processed ou
chrisha
2016/12/21 20:14:59
I did some experiments here.
1. LoadLibrary("foo.
| |
| 196 return; | |
| 197 } | |
| 198 | |
| 199 // The module does not exist, and by definition is smaller in value than | |
| 200 // the largest module ID already present. Add it, and ensure the largest | |
| 201 // module stays at the end. | |
| 202 load_addresses->push_back(load_addresses->back()); | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
is this the same thing?
load_addresses->emplace(
chrisha
2016/12/20 19:46:23
It is indeed.
| |
| 203 auto it = load_addresses->rbegin(); | |
| 204 it++; | |
| 205 it->first = module_id; | |
| 206 it->second = load_address; | |
| 207 | |
| 208 return; | |
| 209 } | |
| 210 | |
| 211 // static | |
| 212 void ModuleDatabase::RemoveLoadAddressById( | |
| 213 ModuleId module_id, | |
| 214 ModuleLoadAddresses* load_addresses) { | |
| 215 if (load_addresses->empty()) | |
| 216 return; | |
| 217 | |
| 218 // Special case: removing the maximum index module. Need to find the new | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
silly idea: how about changing FindLoadAddressInde
chrisha
2016/12/20 19:46:22
No, not silly at all! Done.
| |
| 219 // maximum element and ensure it goes to the end. | |
| 220 if (load_addresses->size() > 2 && load_addresses->back().first == module_id) | |
| 221 return RemoveLoadAddressByIndex(load_addresses->size() - 1, load_addresses); | |
| 222 | |
| 223 // The element to be removed is not the last one. | |
| 224 int i = FindLoadAddressIndexById(module_id, *load_addresses); | |
| 225 RemoveLoadAddressByIndex(i, load_addresses); | |
| 226 } | |
| 227 | |
| 228 // static | |
| 229 void ModuleDatabase::RemoveLoadAddressByIndex( | |
| 230 int index, | |
| 231 ModuleLoadAddresses* load_addresses) { | |
| 232 DCHECK_LE(0, index); | |
| 233 DCHECK_GT(load_addresses->size(), static_cast<size_t>(index)); | |
| 234 | |
| 235 // Special case: removing the last module (with maximum id). Need to find the | |
| 236 // new maximum element and ensure it goes to the end. | |
| 237 if (load_addresses->size() > 2 && | |
| 238 static_cast<size_t>(index + 1) == load_addresses->size()) { | |
| 239 // Find the index of the new maximum element. | |
| 240 ModuleId max_id = -1; // These start at zero. | |
| 241 int max_index = kInvalidIndex; | |
| 242 for (size_t i = 0; i < load_addresses->size() - 1; ++i) { | |
| 243 if (load_addresses->at(i).first > max_id) { | |
| 244 max_id = load_addresses->at(i).first; | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
avoid at() -- it does range checking and throws ex
chrisha
2016/12/20 19:46:23
Another TIL...
Done.
| |
| 245 max_index = i; | |
| 246 } | |
| 247 } | |
| 248 | |
| 249 // Remove the max element. | |
| 250 load_addresses->resize(load_addresses->size() - 1); | |
| 251 | |
| 252 // If the new max element isn't in the last position, then swap it so it is. | |
| 253 int last_index = load_addresses->size() - 1; | |
| 254 if (max_index != last_index) | |
| 255 std::swap((*load_addresses)[max_index], (*load_addresses)[last_index]); | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
#include <algorithm>
chrisha
2016/12/20 19:46:23
Done.
| |
| 256 | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 // If the element to be removed is second last then a single swap is | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
swap -> copy
chrisha
2016/12/20 19:46:23
Done.
| |
| 261 // sufficient. | |
| 262 if (static_cast<size_t>(index + 2) == load_addresses->size()) { | |
| 263 (*load_addresses)[index] = (*load_addresses)[index + 1]; | |
| 264 } else { | |
| 265 // In the general case two swaps are necessary. | |
|
grt (UTC plus 2)
2016/12/20 11:11:33
swaps -> copies
chrisha
2016/12/20 19:46:23
Done.
| |
| 266 int max_index = load_addresses->size() - 1; | |
| 267 (*load_addresses)[index] = (*load_addresses)[max_index - 1]; | |
| 268 (*load_addresses)[max_index - 1] = (*load_addresses)[max_index]; | |
| 269 } | |
| 270 | |
| 271 // Remove the last element, which is now duplicated. | |
| 272 load_addresses->resize(load_addresses->size() - 1); | |
| 273 } | |
| 274 | |
| 275 ModuleDatabase::ModuleInfo* ModuleDatabase::FindOrCreateModuleInfo( | |
| 276 const base::FilePath& module_path) { | |
| 277 ModuleInfo key(module_path, modules_.size()); | |
| 278 auto result = modules_.insert(key); | |
| 279 return const_cast<ModuleInfo*>(&(*result.first)); | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
is this cast really needed? |this| isn't const, an
chrisha
2016/12/20 19:46:23
std::set always returns elements as "const", as th
grt (UTC plus 2)
2016/12/20 21:09:52
Ah. Grn. The critical piece, then, is that the obj
chrisha
2016/12/21 20:14:59
Tried to make this more clear.
| |
| 280 } | |
| 281 | |
| 282 ModuleDatabase::ProcessInfo* ModuleDatabase::GetProcessInfo( | |
| 283 uint32_t process_id) { | |
| 284 ProcessInfo key(process_id, content::PROCESS_TYPE_UNKNOWN); | |
| 285 auto it = processes_.find(key); | |
| 286 if (it == processes_.end()) | |
| 287 return nullptr; | |
| 288 return const_cast<ProcessInfo*>(&(*it)); | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
same comment about cast
chrisha
2016/12/20 19:46:23
Ditto.
| |
| 289 } | |
| 290 | |
| 291 const ModuleDatabase::ProcessInfo* ModuleDatabase::CreateProcessInfo( | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
the only caller DCHECKS the result. how about doin
chrisha
2016/12/20 19:46:22
Actually, that DCHECK is meant to be elided. Made
| |
| 292 uint32_t process_id, | |
| 293 content::ProcessType process_type) { | |
| 294 ProcessInfo key(process_id, process_type); | |
| 295 auto result = processes_.insert(key); | |
| 296 | |
| 297 // If the element was inserted then return it. | |
| 298 if (result.second) | |
| 299 return &(*result.first); | |
| 300 // Otherwise it already existed, so return nullptr. | |
| 301 return nullptr; | |
| 302 } | |
| 303 | |
| 304 bool ModuleDatabase::DeleteProcessInfo(uint32_t process_id) { | |
|
grt (UTC plus 2)
2016/12/20 11:11:34
similar comment: why not DCHECK in here?
chrisha
2016/12/20 19:46:23
Made void, removed the unneeded DCHECK.
| |
| 305 ProcessInfo key(process_id, content::PROCESS_TYPE_UNKNOWN); | |
| 306 auto it = processes_.find(key); | |
| 307 if (it == processes_.end()) | |
| 308 return false; | |
| 309 processes_.erase(it); | |
| 310 return true; | |
| 311 } | |
| 312 | |
| 313 // ModuleDatabase::ModuleInfo -------------------------------------------------- | |
| 314 | |
| 315 ModuleDatabase::ModuleInfo::ModuleInfo(const base::FilePath& module_path, | |
| 316 uint32_t module_id) | |
| 317 : module_path(module_path), module_id(module_id), process_types(0) {} | |
| 318 | |
| 319 bool ModuleDatabase::ModuleInfo::operator<(const ModuleInfo& mi) const { | |
| 320 return module_path < mi.module_path; | |
| 321 } | |
| 322 | |
| 323 // ModuleDatabase::ProcessInfo ------------------------------------------------- | |
| 324 | |
| 325 ModuleDatabase::ProcessInfo::ProcessInfo(uint32_t process_id, | |
| 326 content::ProcessType process_type) | |
| 327 : process_id(process_id), process_type(process_type) {} | |
| 328 | |
| 329 bool ModuleDatabase::ProcessInfo::operator<(const ProcessInfo& pi) const { | |
| 330 return process_id < pi.process_id; | |
| 331 } | |
| OLD | NEW |