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
3 // found in the LICENSE file.
4
5 #include <windows.h>
6 #include <shlwapi.h>
7
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/debug/trace_event.h"
11 #include "base/environment.h"
12 #include "base/file_version_info.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/rand_util.h" // For PreRead experiment.
17 #include "base/sha1.h" // For PreRead experiment.
18 #include "base/strings/string16.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/stringprintf.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/version.h"
23 #include "base/win/windows_version.h"
24 #include "chrome/app/chrome_crash_reporter_client.h"
25 #include "chrome/app/client_util.h"
26 #include "chrome/app/image_pre_reader_win.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/chrome_result_codes.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/env_vars.h"
31 #include "chrome/installer/util/google_update_constants.h"
32 #include "chrome/installer/util/google_update_settings.h"
33 #include "chrome/installer/util/install_util.h"
34 #include "chrome/installer/util/util_constants.h"
35 #include "components/crash/app/breakpad_win.h"
36 #include "components/crash/app/crash_reporter_client.h"
37 #include "components/metrics/client_info.h"
38 #include "content/public/app/startup_helper_win.h"
39 #include "sandbox/win/src/sandbox.h"
40
41 namespace {
42 // The entry point signature of chrome.dll.
43 typedef int (*DLL_MAIN)(HINSTANCE, sandbox::SandboxInterfaceInfo*);
44
45 typedef void (*RelaunchChromeBrowserWithNewCommandLineIfNeededFunc)();
46
47 base::LazyInstance<chrome::ChromeCrashReporterClient>::Leaky
48 g_chrome_crash_client = LAZY_INSTANCE_INITIALIZER;
49
50 // Returns true if the build date for this module precedes the expiry date
51 // for the pre-read experiment.
PreReadExperimentIsActive()52 bool PreReadExperimentIsActive() {
53 const int kPreReadExpiryYear = 2014;
54 const int kPreReadExpiryMonth = 7;
55 const int kPreReadExpiryDay = 1;
56 const char kBuildTimeStr[] = __DATE__ " " __TIME__;
57
58 // Get the timestamp of the build.
59 base::Time build_time;
60 bool result = base::Time::FromString(kBuildTimeStr, &build_time);
61 DCHECK(result);
62
63 // Get the timestamp at which the experiment expires.
64 base::Time::Exploded exploded = {0};
65 exploded.year = kPreReadExpiryYear;
66 exploded.month = kPreReadExpiryMonth;
67 exploded.day_of_month = kPreReadExpiryDay;
68 base::Time expiration_time = base::Time::FromLocalExploded(exploded);
69
70 // Return true if the build time predates the expiration time..
71 return build_time < expiration_time;
72 }
73
74 // Get random unit values, i.e., in the range (0, 1), denoting a die-toss for
75 // being in an experiment population and experimental group thereof.
GetPreReadPopulationAndGroup(double * population,double * group)76 void GetPreReadPopulationAndGroup(double* population, double* group) {
77 // By default we use the metrics id for the user as stable pseudo-random
78 // input to a hash.
79 scoped_ptr<metrics::ClientInfo> client_info =
80 GoogleUpdateSettings::LoadMetricsClientInfo();
81
82 // If this user has no metrics id, we fall back to a purely random value per
83 // browser session.
84 const size_t kLength = 16;
85 std::string random_value(client_info ? client_info->client_id
86 : base::RandBytesAsString(kLength));
87
88 // To interpret the value as a random number we hash it and read the first 8
89 // bytes of the hash as a unit-interval representing a die-toss for being in
90 // the experiment population and the second 8 bytes as a die-toss for being
91 // in various experiment groups.
92 unsigned char sha1_hash[base::kSHA1Length];
93 base::SHA1HashBytes(
94 reinterpret_cast<const unsigned char*>(random_value.c_str()),
95 random_value.size() * sizeof(random_value[0]),
96 sha1_hash);
97 COMPILE_ASSERT(2 * sizeof(uint64) < sizeof(sha1_hash), need_more_data);
98 const uint64* random_bits = reinterpret_cast<uint64*>(&sha1_hash[0]);
99
100 // Convert the bits into unit-intervals and return.
101 *population = base::BitsToOpenEndedUnitInterval(random_bits[0]);
102 *group = base::BitsToOpenEndedUnitInterval(random_bits[1]);
103 }
104
105 // Gets the amount of pre-read to use as well as the experiment group in which
106 // the user falls.
InitPreReadPercentage()107 size_t InitPreReadPercentage() {
108 // By default use the old behaviour: read 100%.
109 const int kDefaultPercentage = 100;
110 const char kDefaultFormatStr[] = "%d-pct-default";
111 const char kControlFormatStr[] = "%d-pct-control";
112 const char kGroupFormatStr[] = "%d-pct";
113
114 COMPILE_ASSERT(kDefaultPercentage <= 100, default_percentage_too_large);
115 COMPILE_ASSERT(kDefaultPercentage % 5 == 0, default_percentage_not_mult_5);
116
117 // Roll the dice to determine if this user is in the experiment and if so,
118 // in which experimental group.
119 double population = 0.0;
120 double group = 0.0;
121 GetPreReadPopulationAndGroup(&population, &group);
122
123 // We limit experiment populations to 1% of the Stable and 10% of each of
124 // the other channels.
125 const base::string16 channel(GoogleUpdateSettings::GetChromeChannel(
126 GoogleUpdateSettings::IsSystemInstall()));
127 double threshold = (channel == installer::kChromeChannelStable) ? 0.01 : 0.10;
128
129 // If the experiment has expired use the default pre-read level. Otherwise,
130 // those not in the experiment population also use the default pre-read level.
131 size_t value = kDefaultPercentage;
132 const char* format_str = kDefaultFormatStr;
133 if (PreReadExperimentIsActive() && (population <= threshold)) {
134 // We divide the experiment population into groups pre-reading at 5 percent
135 // increments in the range [0, 100].
136 value = static_cast<size_t>(group * 21.0) * 5;
137 DCHECK_LE(value, 100u);
138 DCHECK_EQ(0u, value % 5);
139 format_str =
140 (value == kDefaultPercentage) ? kControlFormatStr : kGroupFormatStr;
141 }
142
143 // Generate the group name corresponding to this percentage value.
144 std::string group_name;
145 base::SStringPrintf(&group_name, format_str, value);
146
147 // Persist the group name to the environment so that it can be used for
148 // reporting.
149 scoped_ptr<base::Environment> env(base::Environment::Create());
150 env->SetVar(chrome::kPreReadEnvironmentVariable, group_name);
151
152 // Return the percentage value to be used.
153 return value;
154 }
155
156 // Expects that |dir| has a trailing backslash. |dir| is modified so it
157 // contains the full path that was tried. Caller must check for the return
158 // value not being null to determine if this path contains a valid dll.
LoadModuleWithDirectory(base::string16 * dir,const wchar_t * dll_name,bool pre_read)159 HMODULE LoadModuleWithDirectory(base::string16* dir,
160 const wchar_t* dll_name,
161 bool pre_read) {
162 ::SetCurrentDirectoryW(dir->c_str());
163 dir->append(dll_name);
164
165 if (pre_read) {
166 #if !defined(WIN_DISABLE_PREREAD)
167 // We pre-read the binary to warm the memory caches (fewer hard faults to
168 // page parts of the binary in).
169 const size_t kStepSize = 1024 * 1024;
170 size_t percentage = InitPreReadPercentage();
171 ImagePreReader::PartialPreReadImage(dir->c_str(), percentage, kStepSize);
172 #endif
173 }
174
175 return ::LoadLibraryExW(dir->c_str(), NULL,
176 LOAD_WITH_ALTERED_SEARCH_PATH);
177 }
178
RecordDidRun(const base::string16 & dll_path)179 void RecordDidRun(const base::string16& dll_path) {
180 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
181 GoogleUpdateSettings::UpdateDidRunState(true, system_level);
182 }
183
ClearDidRun(const base::string16 & dll_path)184 void ClearDidRun(const base::string16& dll_path) {
185 bool system_level = !InstallUtil::IsPerUserInstall(dll_path.c_str());
186 GoogleUpdateSettings::UpdateDidRunState(false, system_level);
187 }
188
InMetroMode()189 bool InMetroMode() {
190 return (wcsstr(
191 ::GetCommandLineW(), L" -ServerName:DefaultBrowserServer") != NULL);
192 }
193
194 typedef int (*InitMetro)();
195
196 } // namespace
197
GetExecutablePath()198 base::string16 GetExecutablePath() {
199 wchar_t path[MAX_PATH];
200 ::GetModuleFileNameW(NULL, path, MAX_PATH);
201 if (!::PathRemoveFileSpecW(path))
202 return base::string16();
203 base::string16 exe_path(path);
204 return exe_path.append(1, L'\\');
205 }
206
GetCurrentModuleVersion()207 base::string16 GetCurrentModuleVersion() {
208 scoped_ptr<FileVersionInfo> file_version_info(
209 FileVersionInfo::CreateFileVersionInfoForCurrentModule());
210 if (file_version_info.get()) {
211 base::string16 version_string(file_version_info->file_version());
212 if (Version(base::UTF16ToASCII(version_string)).IsValid())
213 return version_string;
214 }
215 return base::string16();
216 }
217
218 //=============================================================================
219
MainDllLoader()220 MainDllLoader::MainDllLoader()
221 : dll_(NULL), metro_mode_(InMetroMode()) {
222 }
223
~MainDllLoader()224 MainDllLoader::~MainDllLoader() {
225 }
226
227 // Loading chrome is an interesting affair. First we try loading from the
228 // current directory to support run-what-you-compile and other development
229 // scenarios.
230 // If that fails then we look at the version resource in the current
231 // module. This is the expected path for chrome.exe browser instances in an
232 // installed build.
Load(base::string16 * version,base::string16 * out_file)233 HMODULE MainDllLoader::Load(base::string16* version,
234 base::string16* out_file) {
235 const base::string16 executable_dir(GetExecutablePath());
236 *out_file = executable_dir;
237
238 const wchar_t* dll_name = metro_mode_ ?
239 installer::kChromeMetroDll :
240 #if !defined(CHROME_MULTIPLE_DLL)
241 installer::kChromeDll;
242 #else
243 (process_type_ == "service") || process_type_.empty() ?
244 installer::kChromeDll :
245 installer::kChromeChildDll;
246 #endif
247 const bool pre_read = !metro_mode_;
248 HMODULE dll = LoadModuleWithDirectory(out_file, dll_name, pre_read);
249 if (!dll) {
250 base::string16 version_string(GetCurrentModuleVersion());
251 if (version_string.empty()) {
252 LOG(ERROR) << "No valid Chrome version found";
253 return NULL;
254 }
255 *out_file = executable_dir;
256 *version = version_string;
257 out_file->append(version_string).append(1, L'\\');
258 dll = LoadModuleWithDirectory(out_file, dll_name, pre_read);
259 if (!dll) {
260 PLOG(ERROR) << "Failed to load Chrome DLL from " << *out_file;
261 return NULL;
262 }
263 }
264
265 DCHECK(dll);
266 return dll;
267 }
268
269 // Launching is a matter of loading the right dll, setting the CHROME_VERSION
270 // environment variable and just calling the entry point. Derived classes can
271 // add custom code in the OnBeforeLaunch callback.
Launch(HINSTANCE instance)272 int MainDllLoader::Launch(HINSTANCE instance) {
273 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
274 process_type_ = cmd_line.GetSwitchValueASCII(switches::kProcessType);
275
276 base::string16 version;
277 base::string16 file;
278
279 if (metro_mode_) {
280 HMODULE metro_dll = Load(&version, &file);
281 if (!metro_dll)
282 return chrome::RESULT_CODE_MISSING_DATA;
283
284 InitMetro chrome_metro_main =
285 reinterpret_cast<InitMetro>(::GetProcAddress(metro_dll, "InitMetro"));
286 return chrome_metro_main();
287 }
288
289 // Initialize the sandbox services.
290 sandbox::SandboxInterfaceInfo sandbox_info = {0};
291 content::InitializeSandboxInfo(&sandbox_info);
292
293 crash_reporter::SetCrashReporterClient(g_chrome_crash_client.Pointer());
294 bool exit_now = true;
295 if (process_type_.empty()) {
296 if (breakpad::ShowRestartDialogIfCrashed(&exit_now)) {
297 // We restarted because of a previous crash. Ask user if we should
298 // Relaunch. Only for the browser process. See crbug.com/132119.
299 if (exit_now)
300 return content::RESULT_CODE_NORMAL_EXIT;
301 }
302 }
303 breakpad::InitCrashReporter(process_type_);
304
305 dll_ = Load(&version, &file);
306 if (!dll_)
307 return chrome::RESULT_CODE_MISSING_DATA;
308
309 scoped_ptr<base::Environment> env(base::Environment::Create());
310 env->SetVar(chrome::kChromeVersionEnvVar, base::WideToUTF8(version));
311
312 OnBeforeLaunch(file);
313 DLL_MAIN chrome_main =
314 reinterpret_cast<DLL_MAIN>(::GetProcAddress(dll_, "ChromeMain"));
315 int rc = chrome_main(instance, &sandbox_info);
316 return OnBeforeExit(rc, file);
317 }
318
RelaunchChromeBrowserWithNewCommandLineIfNeeded()319 void MainDllLoader::RelaunchChromeBrowserWithNewCommandLineIfNeeded() {
320 if (!dll_)
321 return;
322
323 RelaunchChromeBrowserWithNewCommandLineIfNeededFunc relaunch_function =
324 reinterpret_cast<RelaunchChromeBrowserWithNewCommandLineIfNeededFunc>(
325 ::GetProcAddress(dll_,
326 "RelaunchChromeBrowserWithNewCommandLineIfNeeded"));
327 if (!relaunch_function) {
328 LOG(ERROR) << "Could not find exported function "
329 << "RelaunchChromeBrowserWithNewCommandLineIfNeeded";
330 } else {
331 relaunch_function();
332 }
333 }
334
335 //=============================================================================
336
337 class ChromeDllLoader : public MainDllLoader {
338 protected:
OnBeforeLaunch(const base::string16 & dll_path)339 virtual void OnBeforeLaunch(const base::string16& dll_path) {
340 RecordDidRun(dll_path);
341 }
342
OnBeforeExit(int return_code,const base::string16 & dll_path)343 virtual int OnBeforeExit(int return_code, const base::string16& dll_path) {
344 // NORMAL_EXIT_CANCEL is used for experiments when the user cancels
345 // so we need to reset the did_run signal so omaha does not count
346 // this run as active usage.
347 if (chrome::RESULT_CODE_NORMAL_EXIT_CANCEL == return_code) {
348 ClearDidRun(dll_path);
349 }
350 return return_code;
351 }
352 };
353
354 //=============================================================================
355
356 class ChromiumDllLoader : public MainDllLoader {
357 protected:
OnBeforeLaunch(const base::string16 & dll_path)358 virtual void OnBeforeLaunch(const base::string16& dll_path) OVERRIDE {
359 }
OnBeforeExit(int return_code,const base::string16 & dll_path)360 virtual int OnBeforeExit(int return_code,
361 const base::string16& dll_path) OVERRIDE {
362 return return_code;
363 }
364 };
365
MakeMainDllLoader()366 MainDllLoader* MakeMainDllLoader() {
367 #if defined(GOOGLE_CHROME_BUILD)
368 return new ChromeDllLoader();
369 #else
370 return new ChromiumDllLoader();
371 #endif
372 }
373