| 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 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 53 return a.status > b.status; | 53 return a.status > b.status; |
| 54 | 54 |
| 55 if (a.location == b.location) | 55 if (a.location == b.location) |
| 56 return a.name < b.name; | 56 return a.name < b.name; |
| 57 | 57 |
| 58 return a.location < b.location; | 58 return a.location < b.location; |
| 59 } | 59 } |
| 60 | 60 |
| 61 namespace { | 61 namespace { |
| 62 | 62 |
| 63 // The default amount of time between module inspections. This prevents |
| 64 // certificate inspection and validation from using a large amount of CPU and |
| 65 // battery immediately after startup. |
| 66 constexpr base::TimeDelta kDefaultPerModuleDelay = |
| 67 base::TimeDelta::FromSeconds(1); |
| 68 |
| 63 // A struct to help de-duping modules before adding them to the enumerated | 69 // A struct to help de-duping modules before adding them to the enumerated |
| 64 // modules vector. | 70 // modules vector. |
| 65 struct FindModule { | 71 struct FindModule { |
| 66 public: | 72 public: |
| 67 explicit FindModule(const ModuleEnumerator::Module& x) | 73 explicit FindModule(const ModuleEnumerator::Module& x) |
| 68 : module(x) {} | 74 : module(x) {} |
| 69 bool operator()(const ModuleEnumerator::Module& module_in) const { | 75 bool operator()(const ModuleEnumerator::Module& module_in) const { |
| 70 return (module.location == module_in.location) && | 76 return (module.location == module_in.location) && |
| 71 (module.name == module_in.name); | 77 (module.name == module_in.name); |
| 72 } | 78 } |
| (...skipping 313 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 386 // to them. Remove that. | 392 // to them. Remove that. |
| 387 size_t first_space = module->version.find_first_of(L" "); | 393 size_t first_space = module->version.find_first_of(L" "); |
| 388 if (first_space != base::string16::npos) | 394 if (first_space != base::string16::npos) |
| 389 module->version = module->version.substr(0, first_space); | 395 module->version = module->version.substr(0, first_space); |
| 390 | 396 |
| 391 module->normalized = true; | 397 module->normalized = true; |
| 392 } | 398 } |
| 393 | 399 |
| 394 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) | 400 ModuleEnumerator::ModuleEnumerator(EnumerateModulesModel* observer) |
| 395 : enumerated_modules_(nullptr), | 401 : enumerated_modules_(nullptr), |
| 396 observer_(observer) { | 402 observer_(observer), |
| 403 per_module_delay_(kDefaultPerModuleDelay) { |
| 397 } | 404 } |
| 398 | 405 |
| 399 ModuleEnumerator::~ModuleEnumerator() { | 406 ModuleEnumerator::~ModuleEnumerator() { |
| 400 } | 407 } |
| 401 | 408 |
| 402 void ModuleEnumerator::ScanNow(ModulesVector* list) { | 409 void ModuleEnumerator::ScanNow(ModulesVector* list) { |
| 403 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 410 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 404 enumerated_modules_ = list; | 411 enumerated_modules_ = list; |
| 405 | 412 |
| 406 // This object can't be reaped until it has finished scanning, so its safe | 413 // This object can't be reaped until it has finished scanning, so its safe |
| 407 // to post a raw pointer to another thread. It will simply be leaked if the | 414 // to post a raw pointer to another thread. It will simply be leaked if the |
| 408 // scanning has not been finished before shutdown. | 415 // scanning has not been finished before shutdown. |
| 409 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( | 416 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( |
| 410 FROM_HERE, | 417 FROM_HERE, |
| 411 base::Bind(&ModuleEnumerator::ScanImpl, | 418 base::Bind(&ModuleEnumerator::ScanImplStart, |
| 412 base::Unretained(this)), | 419 base::Unretained(this)), |
| 413 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); | 420 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
| 414 } | 421 } |
| 415 | 422 |
| 416 void ModuleEnumerator::ScanImpl() { | 423 void ModuleEnumerator::SetPerModuleDelayToZero() { |
| 424 // Set the delay to zero so the modules enumerate as quickly as possible. |
| 425 per_module_delay_ = base::TimeDelta::FromSeconds(0); |
| 426 } |
| 427 |
| 428 void ModuleEnumerator::ScanImplStart() { |
| 417 base::TimeTicks start_time = base::TimeTicks::Now(); | 429 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 418 | 430 |
| 419 // The provided destination for the enumerated modules should be empty, as it | 431 // The provided destination for the enumerated modules should be empty, as it |
| 420 // should only be populated once, by a single ModuleEnumerator instance. See | 432 // should only be populated once, by a single ModuleEnumerator instance. See |
| 421 // EnumerateModulesModel::ScanNow for details. | 433 // EnumerateModulesModel::ScanNow for details. |
| 422 DCHECK(enumerated_modules_->empty()); | 434 DCHECK(enumerated_modules_->empty()); |
| 423 | 435 |
| 424 // Make sure the path mapping vector is setup so we can collapse paths. | 436 // Make sure the path mapping vector is setup so we can collapse paths. |
| 425 PreparePathMappings(); | 437 PreparePathMappings(); |
| 426 | 438 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 437 checkpoint2 = base::TimeTicks::Now(); | 449 checkpoint2 = base::TimeTicks::Now(); |
| 438 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateShellExtensions", | 450 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateShellExtensions", |
| 439 checkpoint2 - checkpoint); | 451 checkpoint2 - checkpoint); |
| 440 | 452 |
| 441 checkpoint = checkpoint2; | 453 checkpoint = checkpoint2; |
| 442 EnumerateWinsockModules(); | 454 EnumerateWinsockModules(); |
| 443 checkpoint2 = base::TimeTicks::Now(); | 455 checkpoint2 = base::TimeTicks::Now(); |
| 444 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", | 456 UMA_HISTOGRAM_TIMES("Conflicts.EnumerateWinsockModules", |
| 445 checkpoint2 - checkpoint); | 457 checkpoint2 - checkpoint); |
| 446 | 458 |
| 459 enumeration_total_time_ = base::TimeTicks::Now() - start_time; |
| 460 |
| 461 // Post a delayed task to scan the first module. This forwards directly to |
| 462 // ScanImplFinish if there are no modules to scan. |
| 463 BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( |
| 464 FROM_HERE, |
| 465 base::Bind(&ModuleEnumerator::ScanImplModule, |
| 466 base::Unretained(this), |
| 467 0), |
| 468 per_module_delay_); |
| 469 } |
| 470 |
| 471 void ModuleEnumerator::ScanImplDelay(size_t index) { |
| 472 // Bounce this over to a CONTINUE_ON_SHUTDOWN task in the same pool. This is |
| 473 // necessary to prevent shutdown hangs while inspecting a module. |
| 474 BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior( |
| 475 FROM_HERE, |
| 476 base::Bind(&ModuleEnumerator::ScanImplModule, |
| 477 base::Unretained(this), |
| 478 index), |
| 479 base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN); |
| 480 } |
| 481 |
| 482 void ModuleEnumerator::ScanImplModule(size_t index) { |
| 483 while (index < enumerated_modules_->size()) { |
| 484 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 485 Module& entry = enumerated_modules_->at(index); |
| 486 PopulateModuleInformation(&entry); |
| 487 CollapsePath(&entry); |
| 488 base::TimeDelta elapsed = base::TimeTicks::Now() - start_time; |
| 489 enumeration_inspection_time_ += elapsed; |
| 490 enumeration_total_time_ += elapsed; |
| 491 |
| 492 // With a non-zero delay, bounce back over to ScanImplDelay, which will |
| 493 // bounce back to this function and inspect the next module. |
| 494 if (!per_module_delay_.is_zero()) { |
| 495 BrowserThread::GetBlockingPool()->PostDelayedWorkerTask( |
| 496 FROM_HERE, |
| 497 base::Bind(&ModuleEnumerator::ScanImplDelay, |
| 498 base::Unretained(this), |
| 499 index + 1), |
| 500 per_module_delay_); |
| 501 return; |
| 502 } |
| 503 |
| 504 // If the delay has been set to zero then simply finish the rest of the |
| 505 // enumeration in this already started task. |
| 506 ++index; |
| 507 } |
| 508 |
| 509 // Getting here means that all of the modules have been inspected. |
| 510 return ScanImplFinish(); |
| 511 } |
| 512 |
| 513 void ModuleEnumerator::ScanImplFinish() { |
| 447 // TODO(chrisha): Annotate any modules that are suspicious/bad. | 514 // TODO(chrisha): Annotate any modules that are suspicious/bad. |
| 448 | 515 |
| 449 ReportThirdPartyMetrics(); | 516 ReportThirdPartyMetrics(); |
| 450 | 517 |
| 451 std::sort(enumerated_modules_->begin(), | 518 std::sort(enumerated_modules_->begin(), |
| 452 enumerated_modules_->end(), ModuleSort); | 519 enumerated_modules_->end(), ModuleSort); |
| 453 | 520 |
| 521 UMA_HISTOGRAM_TIMES("Conflicts.EnumerationInspectionTime", |
| 522 enumeration_inspection_time_); |
| 454 UMA_HISTOGRAM_TIMES("Conflicts.EnumerationTotalTime", | 523 UMA_HISTOGRAM_TIMES("Conflicts.EnumerationTotalTime", |
| 455 base::TimeTicks::Now() - start_time); | 524 enumeration_total_time_); |
| 456 | 525 |
| 457 // Send a reply back on the UI thread. The |observer_| outlives this | 526 // Send a reply back on the UI thread. The |observer_| outlives this |
| 458 // enumerator, so posting a raw pointer is safe. This is done last as | 527 // enumerator, so posting a raw pointer is safe. This is done last as |
| 459 // DoneScanning will then reap this ModuleEnumerator. | 528 // DoneScanning will then reap this ModuleEnumerator. |
| 460 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | 529 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 461 base::Bind(&EnumerateModulesModel::DoneScanning, | 530 base::Bind(&EnumerateModulesModel::DoneScanning, |
| 462 base::Unretained(observer_))); | 531 base::Unretained(observer_))); |
| 463 } | 532 } |
| 464 | 533 |
| 465 void ModuleEnumerator::EnumerateLoadedModules() { | 534 void ModuleEnumerator::EnumerateLoadedModules() { |
| 466 // Get all modules in the current process. | 535 // Get all modules in the current process. |
| 467 base::win::ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, | 536 base::win::ScopedHandle snap(::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, |
| 468 ::GetCurrentProcessId())); | 537 ::GetCurrentProcessId())); |
| 469 if (!snap.Get()) | 538 if (!snap.Get()) |
| 470 return; | 539 return; |
| 471 | 540 |
| 472 // Walk the module list. | 541 // Walk the module list. |
| 473 MODULEENTRY32 module = { sizeof(module) }; | 542 MODULEENTRY32 module = { sizeof(module) }; |
| 474 if (!::Module32First(snap.Get(), &module)) | 543 if (!::Module32First(snap.Get(), &module)) |
| 475 return; | 544 return; |
| 476 | 545 |
| 477 do { | 546 do { |
| 478 // It would be weird to present chrome.exe as a loaded module. | 547 // It would be weird to present chrome.exe as a loaded module. |
| 479 if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0) | 548 if (_wcsicmp(chrome::kBrowserProcessExecutableName, module.szModule) == 0) |
| 480 continue; | 549 continue; |
| 481 | 550 |
| 482 Module entry; | 551 Module entry; |
| 483 entry.type = LOADED_MODULE; | 552 entry.type = LOADED_MODULE; |
| 484 entry.location = module.szExePath; | 553 entry.location = module.szExePath; |
| 485 PopulateModuleInformation(&entry); | |
| 486 | |
| 487 NormalizeModule(&entry); | 554 NormalizeModule(&entry); |
| 488 CollapsePath(&entry); | |
| 489 enumerated_modules_->push_back(entry); | 555 enumerated_modules_->push_back(entry); |
| 490 } while (::Module32Next(snap.Get(), &module)); | 556 } while (::Module32Next(snap.Get(), &module)); |
| 491 } | 557 } |
| 492 | 558 |
| 493 void ModuleEnumerator::EnumerateShellExtensions() { | 559 void ModuleEnumerator::EnumerateShellExtensions() { |
| 494 ReadShellExtensions(HKEY_LOCAL_MACHINE); | 560 ReadShellExtensions(HKEY_LOCAL_MACHINE); |
| 495 ReadShellExtensions(HKEY_CURRENT_USER); | 561 ReadShellExtensions(HKEY_CURRENT_USER); |
| 496 } | 562 } |
| 497 | 563 |
| 498 void ModuleEnumerator::ReadShellExtensions(HKEY parent) { | 564 void ModuleEnumerator::ReadShellExtensions(HKEY parent) { |
| 499 base::win::RegistryValueIterator registration(parent, kRegPath); | 565 base::win::RegistryValueIterator registration(parent, kRegPath); |
| 500 while (registration.Valid()) { | 566 while (registration.Valid()) { |
| 501 std::wstring key(std::wstring(L"CLSID\\") + registration.Name() + | 567 std::wstring key(std::wstring(L"CLSID\\") + registration.Name() + |
| 502 L"\\InProcServer32"); | 568 L"\\InProcServer32"); |
| 503 base::win::RegKey clsid; | 569 base::win::RegKey clsid; |
| 504 if (clsid.Open(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ) != ERROR_SUCCESS) { | 570 if (clsid.Open(HKEY_CLASSES_ROOT, key.c_str(), KEY_READ) != ERROR_SUCCESS) { |
| 505 ++registration; | 571 ++registration; |
| 506 continue; | 572 continue; |
| 507 } | 573 } |
| 508 base::string16 dll; | 574 base::string16 dll; |
| 509 if (clsid.ReadValue(L"", &dll) != ERROR_SUCCESS) { | 575 if (clsid.ReadValue(L"", &dll) != ERROR_SUCCESS) { |
| 510 ++registration; | 576 ++registration; |
| 511 continue; | 577 continue; |
| 512 } | 578 } |
| 513 clsid.Close(); | 579 clsid.Close(); |
| 514 | 580 |
| 515 Module entry; | 581 Module entry; |
| 516 entry.type = SHELL_EXTENSION; | 582 entry.type = SHELL_EXTENSION; |
| 517 entry.location = dll; | 583 entry.location = dll; |
| 518 PopulateModuleInformation(&entry); | |
| 519 | |
| 520 NormalizeModule(&entry); | 584 NormalizeModule(&entry); |
| 521 CollapsePath(&entry); | |
| 522 AddToListWithoutDuplicating(entry); | 585 AddToListWithoutDuplicating(entry); |
| 523 | 586 |
| 524 ++registration; | 587 ++registration; |
| 525 } | 588 } |
| 526 } | 589 } |
| 527 | 590 |
| 528 void ModuleEnumerator::EnumerateWinsockModules() { | 591 void ModuleEnumerator::EnumerateWinsockModules() { |
| 529 // Add to this list the Winsock LSP DLLs. | 592 // Add to this list the Winsock LSP DLLs. |
| 530 WinsockLayeredServiceProviderList layered_providers; | 593 WinsockLayeredServiceProviderList layered_providers; |
| 531 GetWinsockLayeredServiceProviders(&layered_providers); | 594 GetWinsockLayeredServiceProviders(&layered_providers); |
| 532 for (size_t i = 0; i < layered_providers.size(); ++i) { | 595 for (size_t i = 0; i < layered_providers.size(); ++i) { |
| 533 Module entry; | 596 Module entry; |
| 534 entry.type = WINSOCK_MODULE_REGISTRATION; | 597 entry.type = WINSOCK_MODULE_REGISTRATION; |
| 535 entry.status = NOT_MATCHED; | 598 entry.status = NOT_MATCHED; |
| 536 entry.normalized = false; | 599 entry.normalized = false; |
| 537 entry.location = layered_providers[i].path; | 600 entry.location = layered_providers[i].path; |
| 538 entry.description = layered_providers[i].name; | 601 entry.description = layered_providers[i].name; |
| 539 entry.recommended_action = NONE; | 602 entry.recommended_action = NONE; |
| 540 entry.duplicate_count = 0; | 603 entry.duplicate_count = 0; |
| 541 | 604 |
| 542 wchar_t expanded[MAX_PATH]; | 605 wchar_t expanded[MAX_PATH]; |
| 543 DWORD size = ExpandEnvironmentStrings( | 606 DWORD size = ExpandEnvironmentStrings( |
| 544 entry.location.c_str(), expanded, MAX_PATH); | 607 entry.location.c_str(), expanded, MAX_PATH); |
| 545 if (size != 0 && size <= MAX_PATH) { | 608 if (size != 0 && size <= MAX_PATH) |
| 546 GetCertificateInfo(base::FilePath(expanded), &entry.cert_info); | 609 entry.location = expanded; |
| 547 } | |
| 548 entry.version = base::IntToString16(layered_providers[i].version); | 610 entry.version = base::IntToString16(layered_providers[i].version); |
| 549 | |
| 550 // Paths have already been collapsed. | |
| 551 NormalizeModule(&entry); | 611 NormalizeModule(&entry); |
| 552 AddToListWithoutDuplicating(entry); | 612 AddToListWithoutDuplicating(entry); |
| 553 } | 613 } |
| 554 } | 614 } |
| 555 | 615 |
| 556 void ModuleEnumerator::PopulateModuleInformation(Module* module) { | 616 void ModuleEnumerator::PopulateModuleInformation(Module* module) { |
| 557 module->status = NOT_MATCHED; | 617 module->status = NOT_MATCHED; |
| 558 module->duplicate_count = 0; | 618 module->duplicate_count = 0; |
| 559 module->normalized = false; | 619 module->normalized = false; |
| 560 GetCertificateInfo(base::FilePath(module->location), &module->cert_info); | 620 GetCertificateInfo(base::FilePath(module->location), &module->cert_info); |
| (...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 749 return modules_to_notify_about_; | 809 return modules_to_notify_about_; |
| 750 } | 810 } |
| 751 | 811 |
| 752 void EnumerateModulesModel::MaybePostScanningTask() { | 812 void EnumerateModulesModel::MaybePostScanningTask() { |
| 753 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 813 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 754 static bool done = false; | 814 static bool done = false; |
| 755 if (!done) { | 815 if (!done) { |
| 756 BrowserThread::PostAfterStartupTask( | 816 BrowserThread::PostAfterStartupTask( |
| 757 FROM_HERE, | 817 FROM_HERE, |
| 758 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), | 818 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI), |
| 759 base::Bind(&EnumerateModulesModel::ScanNow, base::Unretained(this))); | 819 base::Bind(&EnumerateModulesModel::ScanNow, |
| 820 base::Unretained(this), |
| 821 true)); |
| 760 done = true; | 822 done = true; |
| 761 } | 823 } |
| 762 } | 824 } |
| 763 | 825 |
| 764 void EnumerateModulesModel::ScanNow() { | 826 void EnumerateModulesModel::ScanNow(bool background_mode) { |
| 765 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 827 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 766 | 828 |
| 767 // |module_enumerator_| is used as a lock to know whether or not there are | 829 // |module_enumerator_| is used as a lock to know whether or not there are |
| 768 // active/pending blocking pool tasks. If a module enumerator exists then a | 830 // active/pending blocking pool tasks. If a module enumerator exists then a |
| 769 // scan is already underway. Otherwise, either no scan has been completed or | 831 // scan is already underway. Otherwise, either no scan has been completed or |
| 770 // a scan has terminated. | 832 // a scan has terminated. |
| 771 if (module_enumerator_) | 833 if (module_enumerator_) { |
| 834 // If a scan is in progress and this request is for immediate results, then |
| 835 // inform the background scan. This is done without any locks because the |
| 836 // other thread only reads from the value that is being modified, and on |
| 837 // Windows its an atomic write. |
| 838 if (!background_mode) |
| 839 module_enumerator_->SetPerModuleDelayToZero(); |
| 772 return; | 840 return; |
| 841 } |
| 773 | 842 |
| 774 // Only allow a single scan per process lifetime. Immediately notify any | 843 // Only allow a single scan per process lifetime. Immediately notify any |
| 775 // observers that the scan is complete. At this point |enumerated_modules_| is | 844 // observers that the scan is complete. At this point |enumerated_modules_| is |
| 776 // safe to access as no potentially racing blocking pool task can exist. | 845 // safe to access as no potentially racing blocking pool task can exist. |
| 777 if (!enumerated_modules_.empty()) { | 846 if (!enumerated_modules_.empty()) { |
| 778 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); | 847 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); |
| 779 return; | 848 return; |
| 780 } | 849 } |
| 781 | 850 |
| 782 // ScanNow does not block, rather it simply schedules a task. | 851 // ScanNow does not block, rather it simply schedules a task. |
| 783 module_enumerator_.reset(new ModuleEnumerator(this)); | 852 module_enumerator_.reset(new ModuleEnumerator(this)); |
| 853 if (!background_mode) |
| 854 module_enumerator_->SetPerModuleDelayToZero(); |
| 784 module_enumerator_->ScanNow(&enumerated_modules_); | 855 module_enumerator_->ScanNow(&enumerated_modules_); |
| 785 } | 856 } |
| 786 | 857 |
| 787 base::ListValue* EnumerateModulesModel::GetModuleList() { | 858 base::ListValue* EnumerateModulesModel::GetModuleList() { |
| 788 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 859 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 789 | 860 |
| 790 // If a |module_enumerator_| is still around then scanning has not yet | 861 // If a |module_enumerator_| is still around then scanning has not yet |
| 791 // completed, and it is unsafe to read from |enumerated_modules_|. | 862 // completed, and it is unsafe to read from |enumerated_modules_|. |
| 792 if (module_enumerator_.get()) | 863 if (module_enumerator_.get()) |
| 793 return nullptr; | 864 return nullptr; |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 912 module_enumerator_.reset(); | 983 module_enumerator_.reset(); |
| 913 | 984 |
| 914 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", | 985 UMA_HISTOGRAM_COUNTS_100("Conflicts.SuspectedBadModules", |
| 915 suspected_bad_modules_detected_); | 986 suspected_bad_modules_detected_); |
| 916 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", | 987 UMA_HISTOGRAM_COUNTS_100("Conflicts.ConfirmedBadModules", |
| 917 confirmed_bad_modules_detected_); | 988 confirmed_bad_modules_detected_); |
| 918 | 989 |
| 919 // Forward the callback to any registered observers. | 990 // Forward the callback to any registered observers. |
| 920 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); | 991 FOR_EACH_OBSERVER(Observer, observers_, OnScanCompleted()); |
| 921 } | 992 } |
| OLD | NEW |