blob: 748e88216ee734f7b6a191438a96643422450ff8 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/conflicts/third_party_metrics_recorder_win.h"
#include <algorithm>
#include <limits>
#include "base/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string16.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/conflicts/module_info_util_win.h"
#include "chrome/browser/conflicts/module_info_win.h"
#include "components/crash/core/common/crash_key.h"
#if defined(GOOGLE_CHROME_BUILD)
#include "chrome_elf/third_party_dlls/public_api.h"
#endif
namespace {
// Returns true if the module is signed by Google.
bool IsGoogleModule(base::StringPiece16 subject) {
static const wchar_t kGoogle[] = L"Google Inc";
return subject == kGoogle;
}
} // namespace
ThirdPartyMetricsRecorder::ThirdPartyMetricsRecorder() {
current_value_.reserve(kCrashKeySize);
#if defined(GOOGLE_CHROME_BUILD)
// It is safe to use base::Unretained() since the timer is a member variable
// of this class.
heartbeat_metrics_timer_.Start(
FROM_HERE, base::TimeDelta::FromMinutes(5),
base::BindRepeating(&ThirdPartyMetricsRecorder::RecordHeartbeatMetrics,
base::Unretained(this)));
// Emit the result of applying the NtMapViewOfSection hook in chrome_elf.dll.
base::UmaHistogramSparse("ChromeElf.ApplyHookResult", GetApplyHookResult());
#endif
}
ThirdPartyMetricsRecorder::~ThirdPartyMetricsRecorder() = default;
void ThirdPartyMetricsRecorder::OnNewModuleFound(
const ModuleInfoKey& module_key,
const ModuleInfoData& module_data) {
const CertificateInfo& certificate_info =
module_data.inspection_result->certificate_info;
module_count_++;
if (certificate_info.type != CertificateInfo::Type::NO_CERTIFICATE) {
++signed_module_count_;
if (certificate_info.type == CertificateInfo::Type::CERTIFICATE_IN_CATALOG)
++catalog_module_count_;
base::StringPiece16 certificate_subject = certificate_info.subject;
if (IsMicrosoftModule(certificate_subject)) {
++microsoft_module_count_;
} else if (IsGoogleModule(certificate_subject)) {
// No need to count these explicitly.
} else {
// Count modules that are neither signed by Google nor Microsoft.
// These are considered "third party" modules.
if (module_data.module_properties &
ModuleInfoData::kPropertyLoadedModule) {
++loaded_third_party_module_count_;
} else {
++not_loaded_third_party_module_count_;
}
}
} else {
++unsigned_module_count_;
// Put unsigned modules into the crash keys.
if (module_data.module_properties & ModuleInfoData::kPropertyLoadedModule)
AddUnsignedModuleToCrashkeys(module_data.inspection_result->basename);
}
if (module_data.module_properties & ModuleInfoData::kPropertyShellExtension)
shell_extensions_count_++;
}
void ThirdPartyMetricsRecorder::OnModuleDatabaseIdle() {
if (metrics_emitted_)
return;
metrics_emitted_ = true;
// Report back some metrics regarding third party modules and certificates.
base::UmaHistogramCustomCounts("ThirdPartyModules.Modules.Loaded",
loaded_third_party_module_count_, 1, 500, 50);
base::UmaHistogramCustomCounts("ThirdPartyModules.Modules.NotLoaded",
not_loaded_third_party_module_count_, 1, 500,
50);
base::UmaHistogramCustomCounts("ThirdPartyModules.Modules.Signed",
signed_module_count_, 1, 500, 50);
base::UmaHistogramCustomCounts("ThirdPartyModules.Modules.Signed.Microsoft",
microsoft_module_count_, 1, 500, 50);
base::UmaHistogramCustomCounts("ThirdPartyModules.Modules.Signed.Catalog",
catalog_module_count_, 1, 500, 50);
base::UmaHistogramCustomCounts("ThirdPartyModules.Modules.Total",
module_count_, 1, 500, 50);
base::UmaHistogramCustomCounts("ThirdPartyModules.Modules.Unsigned",
unsigned_module_count_, 1, 500, 50);
base::UmaHistogramCounts100("ThirdPartyModules.ShellExtensionsCount3",
shell_extensions_count_);
}
void ThirdPartyMetricsRecorder::AddUnsignedModuleToCrashkeys(
const base::string16& module_basename) {
using UnsignedModulesKey = crash_reporter::CrashKeyString<kCrashKeySize>;
static UnsignedModulesKey unsigned_modules_keys[] = {
{"unsigned-modules-1", UnsignedModulesKey::Tag::kArray},
{"unsigned-modules-2", UnsignedModulesKey::Tag::kArray},
{"unsigned-modules-3", UnsignedModulesKey::Tag::kArray},
{"unsigned-modules-4", UnsignedModulesKey::Tag::kArray},
{"unsigned-modules-5", UnsignedModulesKey::Tag::kArray},
};
if (current_key_index_ >= base::size(unsigned_modules_keys))
return;
std::string module = base::UTF16ToUTF8(module_basename);
// Truncate the basename if it doesn't fit in one crash key.
size_t module_length = std::min(module.length(), kCrashKeySize);
// Check if the module fits in the current string or if a new string is
// needed.
size_t length_remaining = kCrashKeySize;
if (!current_value_.empty())
length_remaining -= current_value_.length() + 1;
if (module_length > length_remaining) {
current_value_.clear();
if (++current_key_index_ >= base::size(unsigned_modules_keys))
return;
}
// Append the module to the current string. Separate with a comma if needed.
if (!current_value_.empty())
current_value_.append(",");
current_value_.append(module, 0, module_length);
unsigned_modules_keys[current_key_index_].Set(current_value_);
}
#if defined(GOOGLE_CHROME_BUILD)
void ThirdPartyMetricsRecorder::RecordHeartbeatMetrics() {
UMA_HISTOGRAM_COUNTS_1M(
"ThirdPartyModules.Heartbeat.UniqueBlockedModulesCount",
GetUniqueBlockedModulesCount());
if (record_blocked_modules_count_) {
uint32_t blocked_modules_count = GetBlockedModulesCount();
UMA_HISTOGRAM_COUNTS_1M("ThirdPartyModules.Heartbeat.BlockedModulesCount",
blocked_modules_count);
// Stop recording when |blocked_modules_count| gets too high. This is to
// avoid dealing with the possible integer overflow that would result in
// emitting wrong values. The exact cutoff point is not important but it
// must be higher than the max value for the histogram (1M in this case).
// It's ok to continue logging the count of unique blocked modules because
// there's no expectation that this count can reach a high value.
if (blocked_modules_count > std::numeric_limits<uint32_t>::max() / 2)
record_blocked_modules_count_ = false;
}
UMA_HISTOGRAM_BOOLEAN(
"ThirdPartyModules.Heartbeat.PrintingWorkaround.BlockingEnabled",
hook_enabled_);
}
#endif