Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(709)

Side by Side Diff: chrome/browser/win/enumerate_modules_model.cc

Issue 2420133002: Make module enumeration a low priority background task. (Closed)
Patch Set: Fix small bug. Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/win/enumerate_modules_model.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « chrome/browser/win/enumerate_modules_model.h ('k') | tools/metrics/histograms/histograms.xml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698