File: third_party_metrics_recorder.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (188 lines) | stat: -rw-r--r-- 7,308 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/win/conflicts/third_party_metrics_recorder.h"

#include <algorithm>
#include <array>
#include <limits>
#include <string>
#include <string_view>

#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "build/branding_buildflags.h"
#include "chrome/browser/win/conflicts/module_info.h"
#include "chrome/browser/win/conflicts/module_info_util.h"
#include "components/crash/core/common/crash_key.h"

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include "chrome/chrome_elf/third_party_dlls/public_api.h"
#endif

namespace {

// Returns true if the module is signed by Google.
bool IsGoogleModule(std::u16string_view subject) {
  static constexpr std::u16string_view kGoogleLlc(u"Google LLC");
  static constexpr std::u16string_view kGoogleInc(u"Google Inc");
  return subject == kGoogleLlc || subject == kGoogleInc;
}

}  // namespace

ThirdPartyMetricsRecorder::ThirdPartyMetricsRecorder() {
  current_value_.reserve(kCrashKeySize);

#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
  // It is safe to use base::Unretained() since the timer is a member variable
  // of this class.
  heartbeat_metrics_timer_.Start(
      FROM_HERE, base::Minutes(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_;

    std::u16string_view 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(
          base::AsWString(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 std::wstring& module_basename) {
  using UnsignedModulesKey = crash_reporter::CrashKeyString<kCrashKeySize>;
  static std::array<UnsignedModulesKey, 5> 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_ >= std::size(unsigned_modules_keys))
    return;

  std::string module = base::WideToUTF8(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_ >= std::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 BUILDFLAG(GOOGLE_CHROME_BRANDING)
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