chrome_elf_init_win.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/bind.h"
6#include "base/metrics/field_trial.h"
7#include "base/metrics/histogram.h"
8#include "base/metrics/sparse_histogram.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/win/registry.h"
11#include "chrome/browser/chrome_elf_init_win.h"
12#include "chrome_elf/blacklist/blacklist.h"
13#include "chrome_elf/chrome_elf_constants.h"
14#include "chrome_elf/dll_hash/dll_hash.h"
15#include "components/variations/variations_associated_data.h"
16#include "content/public/browser/browser_thread.h"
17#include "version.h"  // NOLINT
18
19const char kBrowserBlacklistTrialName[] = "BrowserBlacklist";
20const char kBrowserBlacklistTrialDisabledGroupName[] = "NoBlacklist";
21
22namespace {
23
24// How long to wait, in seconds, before reporting for the second (and last
25// time), what dlls were blocked from the browser process.
26const int kBlacklistReportingDelaySec = 600;
27
28// This enum is used to define the buckets for an enumerated UMA histogram.
29// Hence,
30//   (a) existing enumerated constants should never be deleted or reordered, and
31//   (b) new constants should only be appended in front of
32//       BLACKLIST_SETUP_EVENT_MAX.
33enum BlacklistSetupEventType {
34  // The blacklist beacon has placed to enable the browser blacklisting.
35  BLACKLIST_SETUP_ENABLED = 0,
36
37  // The blacklist was successfully enabled.
38  BLACKLIST_SETUP_RAN_SUCCESSFULLY,
39
40  // The blacklist setup code failed to execute.
41  BLACKLIST_SETUP_FAILED,
42
43  // The blacklist thunk setup code failed. This is probably an indication
44  // that something else patched that code first.
45  BLACKLIST_THUNK_SETUP_FAILED,
46
47  // Deprecated. The blacklist interception code failed to execute.
48  BLACKLIST_INTERCEPTION_FAILED,
49
50  // The blacklist was disabled for this run (after it failed too many times).
51  BLACKLIST_SETUP_DISABLED,
52
53  // Always keep this at the end.
54  BLACKLIST_SETUP_EVENT_MAX,
55};
56
57void RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event) {
58  UMA_HISTOGRAM_ENUMERATION("Blacklist.Setup",
59                            blacklist_setup_event,
60                            BLACKLIST_SETUP_EVENT_MAX);
61}
62
63// Report which DLLs were prevented from being loaded.
64void ReportSuccessfulBlocks() {
65  // Figure out how many dlls were blocked.
66  int num_blocked_dlls = 0;
67  blacklist::SuccessfullyBlocked(NULL, &num_blocked_dlls);
68
69  if (num_blocked_dlls == 0)
70    return;
71
72  // Now retrieve the list of blocked dlls.
73  std::vector<const wchar_t*> blocked_dlls(num_blocked_dlls);
74  blacklist::SuccessfullyBlocked(&blocked_dlls[0], &num_blocked_dlls);
75
76  // Send up the hashes of the blocked dlls via UMA.
77  for (size_t i = 0; i < blocked_dlls.size(); ++i) {
78    std::string dll_name_utf8;
79    base::WideToUTF8(blocked_dlls[i], wcslen(blocked_dlls[i]), &dll_name_utf8);
80    int uma_hash = DllNameToHash(dll_name_utf8);
81
82    UMA_HISTOGRAM_SPARSE_SLOWLY("Blacklist.Blocked", uma_hash);
83  }
84}
85
86}  // namespace
87
88void InitializeChromeElf() {
89  if (base::FieldTrialList::FindFullName(kBrowserBlacklistTrialName) ==
90      kBrowserBlacklistTrialDisabledGroupName) {
91    // Disable the blacklist for all future runs by removing the beacon.
92    base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER);
93    blacklist_registry_key.DeleteKey(blacklist::kRegistryBeaconPath);
94  } else {
95    AddFinchBlacklistToRegistry();
96    BrowserBlacklistBeaconSetup();
97  }
98
99  // Report all successful blacklist interceptions.
100  ReportSuccessfulBlocks();
101
102  // Schedule another task to report all sucessful interceptions later.
103  // This time delay should be long enough to catch any dlls that attempt to
104  // inject after Chrome has started up.
105  content::BrowserThread::PostDelayedTask(
106      content::BrowserThread::UI,
107      FROM_HERE,
108      base::Bind(&ReportSuccessfulBlocks),
109      base::TimeDelta::FromSeconds(kBlacklistReportingDelaySec));
110}
111
112// Note that running multiple chrome instances with distinct user data
113// directories could lead to deletion (and/or replacement) of the finch
114// blacklist registry data in one instance before the second has a chance to
115// read those values.
116void AddFinchBlacklistToRegistry() {
117  base::win::RegKey finch_blacklist_registry_key(
118      HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE);
119
120  // No point in trying to continue if the registry key isn't valid.
121  if (!finch_blacklist_registry_key.Valid())
122    return;
123
124  // Delete and recreate the key to clear the registry.
125  finch_blacklist_registry_key.DeleteKey(L"");
126  finch_blacklist_registry_key.Create(
127      HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE);
128
129  std::map<std::string, std::string> params;
130  variations::GetVariationParams(kBrowserBlacklistTrialName, &params);
131
132  for (std::map<std::string, std::string>::iterator it = params.begin();
133       it != params.end();
134       ++it) {
135    std::wstring name = base::UTF8ToWide(it->first);
136    std::wstring val = base::UTF8ToWide(it->second);
137
138    finch_blacklist_registry_key.WriteValue(name.c_str(), val.c_str());
139  }
140}
141
142void BrowserBlacklistBeaconSetup() {
143  base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER,
144                                           blacklist::kRegistryBeaconPath,
145                                           KEY_QUERY_VALUE | KEY_SET_VALUE);
146
147  // No point in trying to continue if the registry key isn't valid.
148  if (!blacklist_registry_key.Valid())
149    return;
150
151  // Record the results of the last blacklist setup.
152  DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
153  blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state);
154
155  if (blacklist_state == blacklist::BLACKLIST_ENABLED) {
156    // The blacklist setup didn't crash, so we report if it was enabled or not.
157    if (blacklist::IsBlacklistInitialized()) {
158      RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY);
159    } else {
160      // The only way for the blacklist to be enabled, but not fully
161      // initialized is if the thunk setup failed. See blacklist.cc
162      // for more details.
163      RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED);
164    }
165
166    // Regardless of if the blacklist was fully enabled or not, report how many
167    // times we had to try to set it up.
168    DWORD attempt_count = 0;
169    blacklist_registry_key.ReadValueDW(blacklist::kBeaconAttemptCount,
170                                       &attempt_count);
171    UMA_HISTOGRAM_COUNTS_100("Blacklist.RetryAttempts.Success", attempt_count);
172  } else if (blacklist_state == blacklist::BLACKLIST_SETUP_FAILED) {
173    // We can set the state to disabled without checking that the maximum number
174    // of attempts was exceeded because blacklist.cc has already done this.
175    RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED);
176    blacklist_registry_key.WriteValue(blacklist::kBeaconState,
177                                      blacklist::BLACKLIST_DISABLED);
178  } else if (blacklist_state == blacklist::BLACKLIST_DISABLED) {
179    RecordBlacklistSetupEvent(BLACKLIST_SETUP_DISABLED);
180  }
181
182  // Find the last recorded blacklist version.
183  base::string16 blacklist_version;
184  blacklist_registry_key.ReadValue(blacklist::kBeaconVersion,
185                                   &blacklist_version);
186
187  if (blacklist_version != TEXT(CHROME_VERSION_STRING)) {
188    // The blacklist hasn't been enabled for this version yet, so enable it
189    // and reset the failure count to zero.
190    LONG set_version = blacklist_registry_key.WriteValue(
191        blacklist::kBeaconVersion,
192        TEXT(CHROME_VERSION_STRING));
193
194    LONG set_state = blacklist_registry_key.WriteValue(
195        blacklist::kBeaconState,
196        blacklist::BLACKLIST_ENABLED);
197
198    blacklist_registry_key.WriteValue(blacklist::kBeaconAttemptCount,
199                                      static_cast<DWORD>(0));
200
201    // Only report the blacklist as getting setup when both registry writes
202    // succeed, since otherwise the blacklist wasn't properly setup.
203    if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS)
204      RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED);
205  }
206}
207