| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/win/enumerate_modules_model.h" | 5 #include "chrome/browser/win/enumerate_modules_model.h" |
| 6 | 6 |
| 7 #include <softpub.h> | 7 #include <softpub.h> |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <stdint.h> | 9 #include <stdint.h> |
| 10 #include <tlhelp32.h> | 10 #include <tlhelp32.h> |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 #include "base/debug/leak_annotations.h" | 21 #include "base/debug/leak_annotations.h" |
| 22 #include "base/environment.h" | 22 #include "base/environment.h" |
| 23 #include "base/file_version_info.h" | 23 #include "base/file_version_info.h" |
| 24 #include "base/i18n/case_conversion.h" | 24 #include "base/i18n/case_conversion.h" |
| 25 #include "base/macros.h" | 25 #include "base/macros.h" |
| 26 #include "base/metrics/histogram_macros.h" | 26 #include "base/metrics/histogram_macros.h" |
| 27 #include "base/scoped_generic.h" | 27 #include "base/scoped_generic.h" |
| 28 #include "base/strings/string_number_conversions.h" | 28 #include "base/strings/string_number_conversions.h" |
| 29 #include "base/strings/string_util.h" | 29 #include "base/strings/string_util.h" |
| 30 #include "base/strings/utf_string_conversions.h" | 30 #include "base/strings/utf_string_conversions.h" |
| 31 #include "base/threading/sequenced_worker_pool.h" | 31 #include "base/task_scheduler/post_task.h" |
| 32 #include "base/time/time.h" | 32 #include "base/time/time.h" |
| 33 #include "base/values.h" | 33 #include "base/values.h" |
| 34 #include "base/version.h" | 34 #include "base/version.h" |
| 35 #include "base/win/registry.h" | 35 #include "base/win/registry.h" |
| 36 #include "base/win/scoped_handle.h" | 36 #include "base/win/scoped_handle.h" |
| 37 #include "base/win/windows_version.h" | 37 #include "base/win/windows_version.h" |
| 38 #include "chrome/browser/net/service_providers_win.h" | 38 #include "chrome/browser/net/service_providers_win.h" |
| 39 #include "chrome/common/chrome_constants.h" | 39 #include "chrome/common/chrome_constants.h" |
| 40 #include "chrome/common/crash_keys.h" | 40 #include "chrome/common/crash_keys.h" |
| 41 #include "chrome/grit/generated_resources.h" | 41 #include "chrome/grit/generated_resources.h" |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 156 if (first_space != base::string16::npos) | 156 if (first_space != base::string16::npos) |
| 157 module->version = module->version.substr(0, first_space); | 157 module->version = module->version.substr(0, first_space); |
| 158 | 158 |
| 159 // The signer may be returned with trailing nulls. | 159 // The signer may be returned with trailing nulls. |
| 160 size_t first_null = module->cert_info.subject.find(L'\0'); | 160 size_t first_null = module->cert_info.subject.find(L'\0'); |
| 161 if (first_null != base::string16::npos) | 161 if (first_null != base::string16::npos) |
| 162 module->cert_info.subject.resize(first_null); | 162 module->cert_info.subject.resize(first_null); |
| 163 } | 163 } |
| 164 | 164 |
| 165 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) | 165 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) |
| 166 : enumerated_modules_(nullptr), | 166 : background_task_runner_(base::CreateTaskRunnerWithTraits( |
| 167 base::TaskTraits() |
| 168 .MayBlock() |
| 169 .WithPriority(base::TaskPriority::BACKGROUND) |
| 170 .WithShutdownBehavior( |
| 171 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN))), |
| 172 enumerated_modules_(nullptr), |
| 167 observer_(observer), | 173 observer_(observer), |
| 168 per_module_delay_(kDefaultPerModuleDelay) { | 174 per_module_delay_(kDefaultPerModuleDelay) {} |
| 169 } | |
| 170 | 175 |
| 171 ModuleEnumerator::~ModuleEnumerator() { | 176 ModuleEnumerator::~ModuleEnumerator() { |
| 172 } | 177 } |
| 173 | 178 |
| 174 void ModuleEnumerator::ScanNow(ModulesVector* list) { | 179 void ModuleEnumerator::ScanNow(ModulesVector* list) { |
| 175 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 180 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 176 enumerated_modules_ = list; | 181 enumerated_modules_ = list; |
| 177 | 182 |
| 178 // This object can't be reaped until it has finished scanning, so its safe | 183 // This object can't be reaped until it has finished scanning, so its safe |
| 179 // to post a raw pointer to another thread. It will simply be leaked if the | 184 // to post a raw pointer to another thread. It will simply be leaked if the |
| 180 // scanning has not been finished before shutdown. | 185 // scanning has not been finished before shutdown. |
| 181 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( | 186 background_task_runner_->PostTask( |
| 182 FROM_HERE, | 187 FROM_HERE, |
| 183 base::Bind(&ModuleEnumerator::ScanImplStart, | 188 base::Bind(&ModuleEnumerator::ScanImplStart, base::Unretained(this))); |
| 184 base::Unretained(this)), | |
| 185 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
| 186 } | 189 } |
| 187 | 190 |
| 188 void ModuleEnumerator::SetPerModuleDelayToZero() { | 191 void ModuleEnumerator::SetPerModuleDelayToZero() { |
| 189 // Set the delay to zero so the modules enumerate as quickly as possible. | 192 // Set the delay to zero so the modules enumerate as quickly as possible. |
| 190 per_module_delay_ = base::TimeDelta::FromSeconds(0); | 193 per_module_delay_ = base::TimeDelta::FromSeconds(0); |
| 191 } | 194 } |
| 192 | 195 |
| 193 void ModuleEnumerator::ScanImplStart() { | 196 void ModuleEnumerator::ScanImplStart() { |
| 194 base::TimeTicks start_time = base::TimeTicks::Now(); | 197 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 195 | 198 |
| (...skipping 22 matching lines...) Expand all Loading... |
| 218 checkpoint = checkpoint2; | 221 checkpoint = checkpoint2; |
| 219 EnumerateWinsockModules(); | 222 EnumerateWinsockModules(); |
| 220 checkpoint2 = base::TimeTicks::Now(); | 223 checkpoint2 = base::TimeTicks::Now(); |
| 221 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", | 224 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", |
| 222 checkpoint2 - checkpoint); | 225 checkpoint2 - checkpoint); |
| 223 | 226 |
| 224 enumeration_total_time_ = base::TimeTicks::Now() - start_time; | 227 enumeration_total_time_ = base::TimeTicks::Now() - start_time; |
| 225 | 228 |
| 226 // Post a delayed task to scan the first module. This forwards directly to | 229 // Post a delayed task to scan the first module. This forwards directly to |
| 227 // ScanImplFinish if there are no modules to scan. | 230 // ScanImplFinish if there are no modules to scan. |
| 228 BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( | 231 background_task_runner_->PostDelayedTask( |
| 229 FROM_HERE, | 232 FROM_HERE, |
| 230 base::Bind(&ModuleEnumerator::ScanImplModule, | 233 base::Bind(&ModuleEnumerator::ScanImplModule, base::Unretained(this), 0), |
| 231 base::Unretained(this), | |
| 232 0), | |
| 233 per_module_delay_); | 234 per_module_delay_); |
| 234 } | 235 } |
| 235 | 236 |
| 236 void ModuleEnumerator::ScanImplDelay(size_t index) { | |
| 237 // Bounce this over to a CONTINUE_ON_SHUTDOWN task in the same pool. This is | |
| 238 // necessary to prevent shutdown hangs while inspecting a module. | |
| 239 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( | |
| 240 FROM_HERE, | |
| 241 base::Bind(&ModuleEnumerator::ScanImplModule, | |
| 242 base::Unretained(this), | |
| 243 index), | |
| 244 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | |
| 245 } | |
| 246 | |
| 247 void ModuleEnumerator::ScanImplModule(size_t index) { | 237 void ModuleEnumerator::ScanImplModule(size_t index) { |
| 248 while (index < enumerated_modules_->size()) { | 238 while (index < enumerated_modules_->size()) { |
| 249 base::TimeTicks start_time = base::TimeTicks::Now(); | 239 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 250 Module& entry = enumerated_modules_->at(index); | 240 Module& entry = enumerated_modules_->at(index); |
| 251 PopulateModuleInformation(&entry); | 241 PopulateModuleInformation(&entry); |
| 252 NormalizeModule(&entry); | 242 NormalizeModule(&entry); |
| 253 CollapsePath(&entry); | 243 CollapsePath(&entry); |
| 254 base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; | 244 base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; |
| 255 enumeration_inspection_time_ += elapsed; | 245 enumeration_inspection_time_ += elapsed; |
| 256 enumeration_total_time_ += elapsed; | 246 enumeration_total_time_ += elapsed; |
| 257 | 247 |
| 258 // With a non-zero delay, bounce back over to ScanImplDelay, which will | 248 // If |per_module_delay_| is non-zero, post a task to scan the next module |
| 259 // bounce back to this function and inspect the next module. | 249 // when the delay expires. |
| 260 if (!per_module_delay_.is_zero()) { | 250 if (!per_module_delay_.is_zero()) { |
| 261 BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( | 251 background_task_runner_->PostDelayedTask( |
| 262 FROM_HERE, | 252 FROM_HERE, base::Bind(&ModuleEnumerator::ScanImplModule, |
| 263 base::Bind(&ModuleEnumerator::ScanImplDelay, | 253 base::Unretained(this), index + 1), |
| 264 base::Unretained(this), | |
| 265 index + 1), | |
| 266 per_module_delay_); | 254 per_module_delay_); |
| 267 return; | 255 return; |
| 268 } | 256 } |
| 269 | 257 |
| 270 // If the delay has been set to zero then simply finish the rest of the | 258 // If the delay has been set to zero then simply finish the rest of the |
| 271 // enumeration in this already started task. | 259 // enumeration in this already started task. |
| 272 ++index; | 260 ++index; |
| 273 } | 261 } |
| 274 | 262 |
| 275 // Getting here means that all of the modules have been inspected. | 263 // Getting here means that all of the modules have been inspected. |
| (...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 606 base::Unretained(this), | 594 base::Unretained(this), |
| 607 true)); | 595 true)); |
| 608 done = true; | 596 done = true; |
| 609 } | 597 } |
| 610 } | 598 } |
| 611 | 599 |
| 612 void EnumerateModulesModel::ScanNow(bool background_mode) { | 600 void EnumerateModulesModel::ScanNow(bool background_mode) { |
| 613 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 601 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 614 | 602 |
| 615 // |module_enumerator_| is used as a lock to know whether or not there are | 603 // |module_enumerator_| is used as a lock to know whether or not there are |
| 616 // active/pending blocking pool tasks. If a module enumerator exists then a | 604 // active/pending background tasks. If a module enumerator exists then a scan |
| 617 // scan is already underway. Otherwise, either no scan has been completed or | 605 // is already underway. Otherwise, either no scan has been completed or a scan |
| 618 // a scan has terminated. | 606 // has terminated. |
| 619 if (module_enumerator_) { | 607 if (module_enumerator_) { |
| 620 // If a scan is in progress and this request is for immediate results, then | 608 // If a scan is in progress and this request is for immediate results, then |
| 621 // inform the background scan. This is done without any locks because the | 609 // inform the background scan. This is done without any locks because the |
| 622 // other thread only reads from the value that is being modified, and on | 610 // other thread only reads from the value that is being modified, and on |
| 623 // Windows its an atomic write. | 611 // Windows its an atomic write. |
| 624 if (!background_mode) | 612 if (!background_mode) |
| 625 module_enumerator_->SetPerModuleDelayToZero(); | 613 module_enumerator_->SetPerModuleDelayToZero(); |
| 626 return; | 614 return; |
| 627 } | 615 } |
| 628 | 616 |
| 629 // Only allow a single scan per process lifetime. Immediately notify any | 617 // Only allow a single scan per process lifetime. Immediately notify any |
| 630 // observers that the scan is complete. At this point |enumerated_modules_| is | 618 // observers that the scan is complete. At this point |enumerated_modules_| is |
| 631 // safe to access as no potentially racing blocking pool task can exist. | 619 // safe to access as no potentially racing background task can exist. |
| 632 if (!enumerated_modules_.empty()) { | 620 if (!enumerated_modules_.empty()) { |
| 633 for (Observer& observer : observers_) | 621 for (Observer& observer : observers_) |
| 634 observer.OnScanCompleted(); | 622 observer.OnScanCompleted(); |
| 635 return; | 623 return; |
| 636 } | 624 } |
| 637 | 625 |
| 638 // ScanNow does not block, rather it simply schedules a task. | 626 // ScanNow does not block, rather it simply schedules a task. |
| 639 module_enumerator_.reset(new ModuleEnumerator(this)); | 627 module_enumerator_.reset(new ModuleEnumerator(this)); |
| 640 if (!background_mode) | 628 if (!background_mode) |
| 641 module_enumerator_->SetPerModuleDelayToZero(); | 629 module_enumerator_->SetPerModuleDelayToZero(); |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 771 | 759 |
| 772 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", | 760 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", |
| 773 suspected_bad_modules_detected_); | 761 suspected_bad_modules_detected_); |
| 774 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", | 762 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", |
| 775 confirmed_bad_modules_detected_); | 763 confirmed_bad_modules_detected_); |
| 776 | 764 |
| 777 // Forward the callback to any registered observers. | 765 // Forward the callback to any registered observers. |
| 778 for (Observer& observer : observers_) | 766 for (Observer& observer : observers_) |
| 779 observer.OnScanCompleted(); | 767 observer.OnScanCompleted(); |
| 780 } | 768 } |
| OLD | NEW |