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