summaryrefslogtreecommitdiffstats
path: root/chrome_elf
diff options
context:
space:
mode:
authorcaitkp@chromium.org <caitkp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-24 02:12:09 +0000
committercaitkp@chromium.org <caitkp@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-02-24 02:12:09 +0000
commit5a542073d52dd75e4489011e6696451b9281c95a (patch)
tree2c4a00295b08a34c9650fd3b0ed8a0c86593753b /chrome_elf
parent32f3c23a75143288e5c5eaa17fd53d2c224fb17c (diff)
downloadchromium_src-5a542073d52dd75e4489011e6696451b9281c95a.zip
chromium_src-5a542073d52dd75e4489011e6696451b9281c95a.tar.gz
chromium_src-5a542073d52dd75e4489011e6696451b9281c95a.tar.bz2
Breakpad coverage for chrome_elf start up
Review URL: https://codereview.chromium.org/154653002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@252870 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_elf')
-rw-r--r--chrome_elf/DEPS1
-rw-r--r--chrome_elf/blacklist.gypi4
-rw-r--r--chrome_elf/blacklist/blacklist_interceptions.cc65
-rw-r--r--chrome_elf/breakpad.cc180
-rw-r--r--chrome_elf/breakpad.h34
-rw-r--r--chrome_elf/chrome_elf.gyp48
-rw-r--r--chrome_elf/chrome_elf_main.cc10
-rw-r--r--chrome_elf/chrome_elf_util.cc193
-rw-r--r--chrome_elf/chrome_elf_util.h27
-rw-r--r--chrome_elf/chrome_elf_util_unittest.cc186
-rw-r--r--chrome_elf/create_file/chrome_create_file.cc6
-rw-r--r--chrome_elf/create_file/chrome_create_file.h4
-rw-r--r--chrome_elf/create_file/chrome_create_file_unittest.cc6
13 files changed, 724 insertions, 40 deletions
diff --git a/chrome_elf/DEPS b/chrome_elf/DEPS
index ec69c8f..495665e 100644
--- a/chrome_elf/DEPS
+++ b/chrome_elf/DEPS
@@ -1,3 +1,4 @@
include_rules = [
"+sandbox",
+ "+breakpad/src/client",
]
diff --git a/chrome_elf/blacklist.gypi b/chrome_elf/blacklist.gypi
index 2db4941..24fcec6 100644
--- a/chrome_elf/blacklist.gypi
+++ b/chrome_elf/blacklist.gypi
@@ -7,6 +7,7 @@
'target_name': 'blacklist',
'type': 'static_library',
'include_dirs': [
+ '..',
'<(SHARED_INTERMEDIATE_DIR)',
],
'sources': [
@@ -21,7 +22,8 @@
# chrome_elf cannot do.
'../base/base.gyp:base_static',
'../chrome/chrome.gyp:chrome_version_header',
- '../sandbox/sandbox.gyp:sandbox',
+ '../chrome_elf/chrome_elf.gyp:chrome_elf_breakpad',
+ '../sandbox/sandbox.gyp:sandbox',
],
},
{
diff --git a/chrome_elf/blacklist/blacklist_interceptions.cc b/chrome_elf/blacklist/blacklist_interceptions.cc
index 9a3bdab..b3f3d82 100644
--- a/chrome_elf/blacklist/blacklist_interceptions.cc
+++ b/chrome_elf/blacklist/blacklist_interceptions.cc
@@ -17,6 +17,7 @@
#include "base/strings/string16.h"
#include "base/win/pe_image.h"
#include "chrome_elf/blacklist/blacklist.h"
+#include "chrome_elf/breakpad.h"
#include "sandbox/win/src/internal_types.h"
#include "sandbox/win/src/nt_internals.h"
#include "sandbox/win/src/sandbox_nt_util.h"
@@ -167,25 +168,7 @@ bool IsSameAsCurrentProcess(HANDLE process) {
(::GetProcessId(process) == ::GetCurrentProcessId());
}
-} // namespace
-
-namespace blacklist {
-
-bool InitializeInterceptImports() {
- g_nt_query_section_func = reinterpret_cast<NtQuerySectionFunction>(
- GetNtDllExportByName("NtQuerySection"));
- g_nt_query_virtual_memory_func =
- reinterpret_cast<NtQueryVirtualMemoryFunction>(
- GetNtDllExportByName("NtQueryVirtualMemory"));
- g_nt_unmap_view_of_section_func =
- reinterpret_cast<NtUnmapViewOfSectionFunction>(
- GetNtDllExportByName("NtUnmapViewOfSection"));
-
- return g_nt_query_section_func && g_nt_query_virtual_memory_func &&
- g_nt_unmap_view_of_section_func;
-}
-
-SANDBOX_INTERCEPT NTSTATUS WINAPI BlNtMapViewOfSection(
+NTSTATUS BlNtMapViewOfSectionImpl(
NtMapViewOfSectionFunction orig_MapViewOfSection,
HANDLE section,
HANDLE process,
@@ -230,6 +213,50 @@ SANDBOX_INTERCEPT NTSTATUS WINAPI BlNtMapViewOfSection(
return ret;
}
+} // namespace
+
+namespace blacklist {
+
+bool InitializeInterceptImports() {
+ g_nt_query_section_func =
+ reinterpret_cast<NtQuerySectionFunction>(
+ GetNtDllExportByName("NtQuerySection"));
+ g_nt_query_virtual_memory_func =
+ reinterpret_cast<NtQueryVirtualMemoryFunction>(
+ GetNtDllExportByName("NtQueryVirtualMemory"));
+ g_nt_unmap_view_of_section_func =
+ reinterpret_cast<NtUnmapViewOfSectionFunction>(
+ GetNtDllExportByName("NtUnmapViewOfSection"));
+
+ return (g_nt_query_section_func && g_nt_query_virtual_memory_func &&
+ g_nt_unmap_view_of_section_func);
+}
+
+SANDBOX_INTERCEPT NTSTATUS WINAPI BlNtMapViewOfSection(
+ NtMapViewOfSectionFunction orig_MapViewOfSection,
+ HANDLE section,
+ HANDLE process,
+ PVOID *base,
+ ULONG_PTR zero_bits,
+ SIZE_T commit_size,
+ PLARGE_INTEGER offset,
+ PSIZE_T view_size,
+ SECTION_INHERIT inherit,
+ ULONG allocation_type,
+ ULONG protect) {
+ NTSTATUS ret = STATUS_UNSUCCESSFUL;
+
+ __try {
+ ret = BlNtMapViewOfSectionImpl(orig_MapViewOfSection, section, process,
+ base, zero_bits, commit_size, offset,
+ view_size, inherit, allocation_type,
+ protect);
+ } __except(GenerateCrashDump(GetExceptionInformation())) {
+ }
+
+ return ret;
+}
+
#if defined(_WIN64)
NTSTATUS WINAPI BlNtMapViewOfSection64(
HANDLE section, HANDLE process, PVOID *base, ULONG_PTR zero_bits,
diff --git a/chrome_elf/breakpad.cc b/chrome_elf/breakpad.cc
new file mode 100644
index 0000000..d2189b6
--- /dev/null
+++ b/chrome_elf/breakpad.cc
@@ -0,0 +1,180 @@
+// Copyright 2014 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.
+
+// This module contains the necessary code to register the Breakpad exception
+// handler. This implementation is based on Chrome's crash reporting code.
+
+#include "chrome_elf/breakpad.h"
+
+#include <sddl.h>
+
+#include "base/macros.h"
+#include "breakpad/src/client/windows/handler/exception_handler.h"
+#include "chrome_elf/chrome_elf_util.h"
+#include "version.h" // NOLINT
+
+google_breakpad::ExceptionHandler* g_elf_breakpad = NULL;
+
+namespace {
+
+const wchar_t kBreakpadProductName[] = L"ChromeElf";
+const wchar_t kBreakpadVersionEntry[] = L"ver";
+const wchar_t kBreakpadProdEntry[] = L"prod";
+const wchar_t kBreakpadPlatformEntry[] = L"plat";
+const wchar_t kBreakpadPlatformWin32[] = L"Win32";
+
+// The protocol for connecting to the out-of-process Breakpad crash
+// reporter is different for x86-32 and x86-64: the message sizes
+// are different because the message struct contains a pointer. As
+// a result, there are two different named pipes to connect to. The
+// 64-bit one is distinguished with an "-x64" suffix.
+const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices\\";
+const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
+const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
+
+const wchar_t kNoErrorDialogs[] = L"noerrdialogs";
+const wchar_t kChromeHeadless[] = L"CHROME_HEADLESS";
+
+google_breakpad::CustomClientInfo* GetCustomInfo() {
+ static google_breakpad::CustomInfoEntry ver_entry(
+ kBreakpadVersionEntry, TEXT(CHROME_VERSION_STRING));
+ static google_breakpad::CustomInfoEntry prod_entry(
+ kBreakpadProdEntry, kBreakpadProductName);
+ static google_breakpad::CustomInfoEntry plat_entry(
+ kBreakpadPlatformEntry, kBreakpadPlatformWin32);
+ static google_breakpad::CustomInfoEntry entries[] = {
+ ver_entry, prod_entry, plat_entry };
+ static google_breakpad::CustomClientInfo custom_info = {
+ entries, arraysize(entries) };
+ return &custom_info;
+}
+
+base::string16 GetUserSidString() {
+ // Get the current token.
+ HANDLE token = NULL;
+ base::string16 user_sid;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
+ return user_sid;
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ BYTE user_bytes[sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE] = {};
+ TOKEN_USER* user = reinterpret_cast<TOKEN_USER*>(user_bytes);
+
+ wchar_t* sid_string = NULL;
+ if (::GetTokenInformation(token, TokenUser, user, size, &size) &&
+ user->User.Sid &&
+ ::ConvertSidToStringSid(user->User.Sid, &sid_string)) {
+ user_sid = sid_string;
+ ::LocalFree(sid_string);
+ }
+
+ CloseHandle(token);
+ return user_sid;
+}
+
+bool IsHeadless() {
+ DWORD ret = ::GetEnvironmentVariable(L"CHROME_HEADLESS", NULL, 0);
+ if (ret != 0)
+ return true;
+
+ wchar_t* command_line = ::GetCommandLine();
+
+ // Note: Since this is a pure substring search rather than a check for a
+ // switch, there is a small chance that this code will match things that the
+ // Chrome code (which executes a similar check) does not. However, as long as
+ // no other switches contain the string "noerrdialogs", it should not be an
+ // issue.
+ return (command_line && wcsstr(command_line, kNoErrorDialogs));
+}
+
+} // namespace
+
+int GenerateCrashDump(EXCEPTION_POINTERS* exinfo) {
+ DWORD code = exinfo->ExceptionRecord->ExceptionCode;
+ if (code == EXCEPTION_BREAKPOINT || code == EXCEPTION_SINGLE_STEP)
+ return EXCEPTION_CONTINUE_SEARCH;
+
+ if (g_elf_breakpad != NULL)
+ g_elf_breakpad->WriteMinidumpForException(exinfo);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+void InitializeCrashReporting() {
+ wchar_t exe_path[MAX_PATH] = {};
+ if(!::GetModuleFileName(NULL, exe_path, arraysize(exe_path)))
+ return;
+
+ // Disable the message box for assertions.
+ _CrtSetReportMode(_CRT_ASSERT, 0);
+
+ // Get the alternate dump directory. We use the temp path.
+ // N.B. We don't use base::GetTempDir() here to avoid running more code then
+ // necessary before crashes can be properly reported.
+ wchar_t temp_directory[MAX_PATH + 1] = {};
+ DWORD length = GetTempPath(MAX_PATH, temp_directory);
+ if (length == 0)
+ return;
+
+ // Minidump with stacks, PEB, TEBs and unloaded module list.
+ MINIDUMP_TYPE dump_type = static_cast<MINIDUMP_TYPE>(
+ MiniDumpWithProcessThreadData | // Get PEB and TEB.
+ MiniDumpWithUnloadedModules | // Get unloaded modules when available.
+ MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by
+ // stack.
+
+ // Convert #define to a variable so that we can use if() rather than
+ // #if below and so at least compile-test the Chrome code in
+ // Chromium builds.
+#if defined(GOOGLE_CHROME_BUILD)
+ bool is_chrome_build = true;
+#else
+ bool is_chrome_build = false;
+#endif
+
+ base::string16 pipe_name;
+
+ bool enabled_by_policy = false;
+ bool use_policy = ReportingIsEnforcedByPolicy(&enabled_by_policy);
+
+ if (!use_policy && IsHeadless()) {
+ pipe_name = kChromePipeName;
+ } else if (use_policy ? enabled_by_policy :
+ (is_chrome_build && AreUsageStatsEnabled(exe_path))) {
+ // Build the pipe name. It can be one of:
+ // 32-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18
+ // 32-bit user: \\.\pipe\GoogleCrashServices\<user SID>
+ // 64-bit system: \\.\pipe\GoogleCrashServices\S-1-5-18-x64
+ // 64-bit user: \\.\pipe\GoogleCrashServices\<user SID>-x64
+ base::string16 user_sid = IsSystemInstall(exe_path) ? kSystemPrincipalSid :
+ GetUserSidString();
+ if (user_sid.empty())
+ return;
+
+ pipe_name = kGoogleUpdatePipeName;
+ pipe_name += user_sid;
+
+#if defined(_WIN64)
+ pipe_name += L"-x64";
+#endif
+ } else {
+ // Either this is a Chromium build, reporting is disabled by policy or the
+ // user has not given consent.
+ return;
+ }
+
+ g_elf_breakpad = new google_breakpad::ExceptionHandler(
+ temp_directory,
+ NULL,
+ NULL,
+ NULL,
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ dump_type,
+ pipe_name.c_str(),
+ GetCustomInfo());
+
+ if (g_elf_breakpad->IsOutOfProcess()) {
+ // Tells breakpad to handle breakpoint and single step exceptions.
+ g_elf_breakpad->set_handle_debug_exceptions(true);
+ }
+}
diff --git a/chrome_elf/breakpad.h b/chrome_elf/breakpad.h
new file mode 100644
index 0000000..8067708
--- /dev/null
+++ b/chrome_elf/breakpad.h
@@ -0,0 +1,34 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_ELF_BREAKPAD_H_
+#define CHROME_ELF_BREAKPAD_H_
+
+#include <windows.h>
+
+namespace google_breakpad {
+class ExceptionHandler;
+}
+
+// Initializes collection and upload of crash reports. This will only be done if
+// the user has agreed to crash dump reporting.
+//
+// Crash reporting has to be initialized as early as possible (e.g., the first
+// thing in main()) to catch crashes occuring during process startup.
+// Crashes which occur during the global static construction phase will not
+// be caught and reported. This should not be a problem as static non-POD
+// objects are not allowed by the style guide and exceptions to this rule are
+// rare.
+void InitializeCrashReporting();
+
+// Generates a crashdump for the provided |exinfo|. This crashdump will be
+// either be saved locally, or uploaded, depending on how the ExceptionHandler
+// has been configured.
+int GenerateCrashDump(EXCEPTION_POINTERS* exinfo);
+
+// Global pointer to the ExceptionHandler. This is initialized by
+// InitializeCrashReporting() and used by GenerateCrashDump() to record dumps.
+extern google_breakpad::ExceptionHandler* g_elf_breakpad;
+
+#endif // CHROME_ELF_BREAKPAD_H_
diff --git a/chrome_elf/chrome_elf.gyp b/chrome_elf/chrome_elf.gyp
index acf84ce..00cee29 100644
--- a/chrome_elf/chrome_elf.gyp
+++ b/chrome_elf/chrome_elf.gyp
@@ -24,6 +24,7 @@
],
'dependencies': [
'blacklist',
+ 'chrome_elf_breakpad',
'chrome_elf_lib',
],
'msvs_settings': {
@@ -46,6 +47,7 @@
'type': 'executable',
'sources': [
'blacklist/test/blacklist_test.cc',
+ 'chrome_elf_util_unittest.cc',
'create_file/chrome_create_file_unittest.cc',
'elf_imports_unittest.cc',
'ntdll_cache_unittest.cc',
@@ -96,14 +98,14 @@
'..',
],
'sources': [
- 'chrome_elf_constants.cc',
- 'chrome_elf_constants.h',
- 'chrome_elf_types.h',
'create_file/chrome_create_file.cc',
'create_file/chrome_create_file.h',
'ntdll_cache.cc',
'ntdll_cache.h',
],
+ 'dependencies': [
+ 'chrome_elf_common',
+ ],
'conditions': [
['component=="shared_library"', {
# In component builds, all targets depend on chrome_redirects by
@@ -114,6 +116,46 @@
}],
],
},
+ {
+ 'target_name': 'chrome_elf_common',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'chrome_elf_constants.cc',
+ 'chrome_elf_constants.h',
+ 'chrome_elf_types.h',
+ 'chrome_elf_util.cc',
+ 'chrome_elf_util.h',
+ ],
+ 'conditions': [
+ ['component=="shared_library"', {
+ # In component builds, all targets depend on chrome_redirects by
+ # default. Remove it here so we are able to test it.
+ 'dependencies!': [
+ '../chrome_elf/chrome_elf.gyp:chrome_redirects',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'chrome_elf_breakpad',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ '<(SHARED_INTERMEDIATE_DIR)',
+ ],
+ 'sources': [
+ 'breakpad.cc',
+ 'breakpad.h',
+ ],
+ 'dependencies': [
+ 'chrome_elf_common',
+ '../breakpad/breakpad.gyp:breakpad_handler',
+ '../chrome/chrome.gyp:chrome_version_header',
+ ],
+ },
], # targets
'conditions': [
['component=="shared_library"', {
diff --git a/chrome_elf/chrome_elf_main.cc b/chrome_elf/chrome_elf_main.cc
index 772c906..de64375 100644
--- a/chrome_elf/chrome_elf_main.cc
+++ b/chrome_elf/chrome_elf_main.cc
@@ -7,6 +7,7 @@
#include "chrome_elf/chrome_elf_main.h"
#include "chrome_elf/blacklist/blacklist.h"
+#include "chrome_elf/breakpad.h"
#include "chrome_elf/ntdll_cache.h"
void SignalChromeElf() {
@@ -15,8 +16,13 @@ void SignalChromeElf() {
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
if (reason == DLL_PROCESS_ATTACH) {
- InitCache();
- blacklist::Initialize(false); // Don't force, abort if beacon is present.
+ InitializeCrashReporting();
+
+ __try {
+ InitCache();
+ blacklist::Initialize(false); // Don't force, abort if beacon is present.
+ } __except(GenerateCrashDump(GetExceptionInformation())) {
+ }
// TODO(csharp): Move additions to the DLL blacklist to a sane place.
// blacklist::AddDllToBlacklist(L"foo.dll");
diff --git a/chrome_elf/chrome_elf_util.cc b/chrome_elf/chrome_elf_util.cc
new file mode 100644
index 0000000..fc99ae4
--- /dev/null
+++ b/chrome_elf/chrome_elf_util.cc
@@ -0,0 +1,193 @@
+// Copyright 2014 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_elf/chrome_elf_util.h"
+
+#include <windows.h>
+
+#include "base/macros.h"
+#include "base/strings/string16.h"
+
+namespace {
+
+const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState";
+const wchar_t kRegPathClientStateMedium[] =
+ L"Software\\Google\\Update\\ClientStateMedium";
+#if defined(GOOGLE_CHROME_BUILD)
+const wchar_t kRegPathChromePolicy[] = L"SOFTWARE\\Policies\\Google\\Chrome";
+#else
+const wchar_t kRegPathChromePolicy[] = L"SOFTWARE\\Policies\\Chromium";
+#endif // defined(GOOGLE_CHROME_BUILD)
+
+const wchar_t kRegValueUsageStats[] = L"usagestats";
+const wchar_t kUninstallArgumentsField[] = L"UninstallArguments";
+const wchar_t kMetricsReportingEnabled[] =L"MetricsReportingEnabled";
+
+const wchar_t kAppGuidCanary[] =
+ L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}";
+const wchar_t kAppGuidGoogleChrome[] =
+ L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
+const wchar_t kAppGuidGoogleBinaries[] =
+ L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
+
+bool ReadKeyValueString(bool system_install, const wchar_t* key_path,
+ const wchar_t* guid, const wchar_t* value_to_read,
+ base::string16* value_out) {
+ HKEY key = NULL;
+ value_out->clear();
+
+ base::string16 full_key_path(key_path);
+ full_key_path.append(1, L'\\');
+ full_key_path.append(guid);
+
+ if (::RegOpenKeyEx(system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ full_key_path.c_str(), 0,
+ KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key) !=
+ ERROR_SUCCESS) {
+ return false;
+ }
+
+ const size_t kMaxStringLength = 1024;
+ wchar_t raw_value[kMaxStringLength] = {};
+ DWORD size = sizeof(raw_value);
+ DWORD type = REG_SZ;
+ LONG result = ::RegQueryValueEx(key, value_to_read, 0, &type,
+ reinterpret_cast<LPBYTE>(raw_value), &size);
+
+ if (result == ERROR_SUCCESS) {
+ if (type != REG_SZ || (size & 1) != 0) {
+ result = ERROR_NOT_SUPPORTED;
+ } else if (size == 0) {
+ *raw_value = L'\0';
+ } else if (raw_value[size / sizeof(wchar_t) - 1] != L'\0') {
+ if ((size / sizeof(wchar_t)) < kMaxStringLength)
+ raw_value[size / sizeof(wchar_t)] = L'\0';
+ else
+ result = ERROR_MORE_DATA;
+ }
+ }
+
+ if (result == ERROR_SUCCESS)
+ *value_out = raw_value;
+
+ ::RegCloseKey(key);
+
+ return result == ERROR_SUCCESS;
+}
+
+bool ReadKeyValueDW(bool system_install, const wchar_t* key_path,
+ base::string16 guid, const wchar_t* value_to_read,
+ DWORD* value_out) {
+ HKEY key = NULL;
+ *value_out = 0;
+
+ base::string16 full_key_path(key_path);
+ full_key_path.append(1, L'\\');
+ full_key_path.append(guid);
+
+ if (::RegOpenKeyEx(system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ full_key_path.c_str(), 0,
+ KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key) !=
+ ERROR_SUCCESS) {
+ return false;
+ }
+
+ DWORD size = sizeof(*value_out);
+ DWORD type = REG_DWORD;
+ LONG result = ::RegQueryValueEx(key, value_to_read, 0, &type,
+ reinterpret_cast<BYTE*>(value_out), &size);
+
+ ::RegCloseKey(key);
+
+ return result == ERROR_SUCCESS && size == sizeof(*value_out);
+}
+
+} // namespace
+
+bool IsCanary(const wchar_t* exe_path) {
+ return wcsstr(exe_path, L"Chrome SxS\\Application") != NULL;
+}
+
+bool IsSystemInstall(const wchar_t* exe_path) {
+ wchar_t program_dir[MAX_PATH] = {};
+ DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir,
+ arraysize(program_dir));
+ if (ret && ret < MAX_PATH && !wcsncmp(exe_path, program_dir, ret))
+ return true;
+
+ ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir,
+ arraysize(program_dir));
+ if (ret && ret < MAX_PATH && !wcsncmp(exe_path, program_dir, ret))
+ return true;
+
+ return false;
+}
+
+bool IsMultiInstall(bool is_system_install) {
+ base::string16 args;
+ if (!ReadKeyValueString(is_system_install, kRegPathClientState,
+ kAppGuidGoogleChrome, kUninstallArgumentsField,
+ &args)) {
+ return false;
+ }
+ return args.find(L"--multi-install") != base::string16::npos;
+}
+
+bool AreUsageStatsEnabled(const wchar_t* exe_path) {
+ bool enabled = true;
+ bool controlled_by_policy = ReportingIsEnforcedByPolicy(&enabled);
+
+ if (controlled_by_policy && !enabled)
+ return false;
+
+ bool system_install = IsSystemInstall(exe_path);
+ base::string16 app_guid;
+
+ if (IsCanary(exe_path)) {
+ app_guid = kAppGuidCanary;
+ } else {
+ app_guid = IsMultiInstall(system_install) ? kAppGuidGoogleBinaries :
+ kAppGuidGoogleChrome;
+ }
+
+ DWORD out_value = 0;
+ if (system_install &&
+ ReadKeyValueDW(system_install, kRegPathClientStateMedium, app_guid,
+ kRegValueUsageStats, &out_value)) {
+ return out_value == 1;
+ }
+
+ return ReadKeyValueDW(system_install, kRegPathClientState, app_guid,
+ kRegValueUsageStats, &out_value) && out_value == 1;
+}
+
+bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled) {
+ HKEY key = NULL;
+ DWORD value = 0;
+ BYTE* value_bytes = reinterpret_cast<BYTE*>(&value);
+ DWORD size = sizeof(value);
+ DWORD type = REG_DWORD;
+
+ if (::RegOpenKeyEx(HKEY_LOCAL_MACHINE, kRegPathChromePolicy, 0,
+ KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
+ if (::RegQueryValueEx(key, kMetricsReportingEnabled, 0, &type,
+ value_bytes, &size) == ERROR_SUCCESS) {
+ *breakpad_enabled = value != 0;
+ }
+ ::RegCloseKey(key);
+ return size == sizeof(value);
+ }
+
+ if (::RegOpenKeyEx(HKEY_CURRENT_USER, kRegPathChromePolicy, 0,
+ KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
+ if (::RegQueryValueEx(key, kMetricsReportingEnabled, 0, &type,
+ value_bytes, &size) == ERROR_SUCCESS) {
+ *breakpad_enabled = value != 0;
+ }
+ ::RegCloseKey(key);
+ return size == sizeof(value);
+ }
+
+ return false;
+}
diff --git a/chrome_elf/chrome_elf_util.h b/chrome_elf/chrome_elf_util.h
new file mode 100644
index 0000000..a81e54f
--- /dev/null
+++ b/chrome_elf/chrome_elf_util.h
@@ -0,0 +1,27 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_ELF_CHROME_ELF_UTIL_H_
+#define CHROME_ELF_CHROME_ELF_UTIL_H_
+
+#include "base/strings/string16.h"
+
+// Returns true if |exe_path| points to a Chrome installed in an SxS
+// installation.
+bool IsCanary(const wchar_t* exe_path);
+
+// Returns true if |exe_path| points to a per-user level Chrome installation.
+bool IsSystemInstall(const wchar_t* exe_path);
+
+// Returns true if current installation of Chrome is a multi-install.
+bool IsMultiInstall(bool is_system_install);
+
+// Returns true if usage stats collecting is enabled for this user.
+bool AreUsageStatsEnabled(const wchar_t* exe_path);
+
+// Returns true if a policy is in effect. |breakpad_enabled| will be set to true
+// if stats collecting is permitted by this policy and false if not.
+bool ReportingIsEnforcedByPolicy(bool* breakpad_enabled);
+
+#endif // CHROME_ELF_CHROME_ELF_UTIL_H_
diff --git a/chrome_elf/chrome_elf_util_unittest.cc b/chrome_elf/chrome_elf_util_unittest.cc
new file mode 100644
index 0000000..1cb6488
--- /dev/null
+++ b/chrome_elf/chrome_elf_util_unittest.cc
@@ -0,0 +1,186 @@
+// Copyright 2014 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_elf/chrome_elf_util.h"
+
+#include <tuple>
+
+#include "base/test/test_reg_util_win.h"
+#include "base/win/registry.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+namespace {
+
+const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState";
+const wchar_t kRegPathClientStateMedium[] =
+ L"Software\\Google\\Update\\ClientStateMedium";
+const wchar_t kRegValueUsageStats[] = L"usagestats";
+const wchar_t kUninstallArgumentsField[] = L"UninstallArguments";
+
+const wchar_t kAppGuidCanary[] =
+ L"{4ea16ac7-fd5a-47c3-875b-dbf4a2008c20}";
+const wchar_t kAppGuidGoogleChrome[] =
+ L"{8A69D345-D564-463c-AFF1-A69D9E530F96}";
+const wchar_t kAppGuidGoogleBinaries[] =
+ L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}";
+
+const wchar_t kCanaryExePath[] =
+ L"C:\\Users\\user\\AppData\\Local\\Google\\Chrome SxS\\Application"
+ L"\\chrome.exe";
+const wchar_t kChromeSystemExePath[] =
+ L"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";
+const wchar_t kChromeUserExePath[] =
+ L"C:\\Users\\user\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe";
+const wchar_t kChromiumExePath[] =
+ L"C:\\Users\\user\\AppData\\Local\\Chromium\\Application\\chrome.exe";
+
+
+TEST(ChromeElfUtilTest, CanaryTest) {
+ EXPECT_TRUE(IsCanary(kCanaryExePath));
+ EXPECT_FALSE(IsCanary(kChromeUserExePath));
+ EXPECT_FALSE(IsCanary(kChromiumExePath));
+}
+
+TEST(ChromeElfUtilTest, SystemInstallTest) {
+ EXPECT_TRUE(IsSystemInstall(kChromeSystemExePath));
+ EXPECT_FALSE(IsSystemInstall(kChromeUserExePath));
+}
+
+// Parameterized test with paramters:
+// 1: product: "canary" or "google"
+// 2: install level: "user" or "system"
+// 3: install mode: "single" or "multi"
+class ChromeElfUtilTest :
+ public testing::TestWithParam<std::tuple<const char*,
+ const char*,
+ const char*> > {
+ protected:
+ virtual void SetUp() OVERRIDE {
+ override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE,
+ L"chrome_elf_test_local");
+ override_manager_.OverrideRegistry(HKEY_CURRENT_USER,
+ L"chrome_elf_test_current");
+ const char* app;
+ const char* level;
+ const char* mode;
+ std::tie(app, level, mode) = GetParam();
+ is_canary_ = (std::string(app) == "canary");
+ system_level_ = (std::string(level) != "user");
+ multi_install_ = (std::string(mode) != "single");
+ if (is_canary_) {
+ ASSERT_FALSE(system_level_);
+ ASSERT_FALSE(multi_install_);
+ app_guid_ = kAppGuidCanary;
+ chrome_path_ = kCanaryExePath;
+ } else {
+ app_guid_ = kAppGuidGoogleChrome;
+ chrome_path_ = (system_level_ ? kChromeSystemExePath :
+ kChromeUserExePath);
+ }
+ if (multi_install_) {
+ SetMultiInstallStateInRegistry(system_level_, true);
+ app_guid_ = kAppGuidGoogleBinaries;
+ }
+ }
+
+ base::string16 BuildKey(const wchar_t* path, const wchar_t* guid) {
+ base::string16 full_key_path(path);
+ full_key_path.append(1, L'\\');
+ full_key_path.append(guid);
+ return full_key_path;
+ }
+
+ void SetUsageStat(DWORD value, bool state_medium) {
+ LONG result = base::win::RegKey(
+ system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ BuildKey(state_medium ? kRegPathClientStateMedium : kRegPathClientState,
+ app_guid_).c_str(),
+ KEY_SET_VALUE).WriteValue(kRegValueUsageStats, value);
+ ASSERT_EQ(ERROR_SUCCESS, result);
+ }
+
+ void SetMultiInstallStateInRegistry(bool system_install, bool multi) {
+ base::win::RegKey key(
+ system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+ BuildKey(kRegPathClientState, kAppGuidGoogleChrome).c_str(),
+ KEY_SET_VALUE);
+ LONG result;
+ if (multi) {
+ result = key.WriteValue(kUninstallArgumentsField,
+ L"yadda yadda --multi-install yadda yadda");
+ } else {
+ result = key.DeleteValue(kUninstallArgumentsField);
+ }
+ ASSERT_EQ(ERROR_SUCCESS, result);
+ }
+
+ const wchar_t* app_guid_;
+ const wchar_t* chrome_path_;
+ bool system_level_;
+ bool multi_install_;
+ bool is_canary_;
+ registry_util::RegistryOverrideManager override_manager_;
+};
+
+TEST_P(ChromeElfUtilTest, MultiInstallTest) {
+ if (is_canary_)
+ return;
+ SetMultiInstallStateInRegistry(system_level_, true);
+ EXPECT_TRUE(IsMultiInstall(system_level_));
+
+ SetMultiInstallStateInRegistry(system_level_, false);
+ EXPECT_FALSE(IsMultiInstall(system_level_));
+}
+
+TEST_P(ChromeElfUtilTest, UsageStatsAbsent) {
+ EXPECT_FALSE(AreUsageStatsEnabled(chrome_path_));
+}
+
+TEST_P(ChromeElfUtilTest, UsageStatsZero) {
+ SetUsageStat(0, false);
+ EXPECT_FALSE(AreUsageStatsEnabled(chrome_path_));
+}
+
+TEST_P(ChromeElfUtilTest, UsageStatsOne) {
+ SetUsageStat(1, false);
+ EXPECT_TRUE(AreUsageStatsEnabled(chrome_path_));
+ if (is_canary_) {
+ EXPECT_FALSE(AreUsageStatsEnabled(kChromeUserExePath));
+ EXPECT_FALSE(AreUsageStatsEnabled(kChromeSystemExePath));
+ } else if (system_level_) {
+ EXPECT_FALSE(AreUsageStatsEnabled(kCanaryExePath));
+ EXPECT_FALSE(AreUsageStatsEnabled(kChromeUserExePath));
+ } else {
+ EXPECT_FALSE(AreUsageStatsEnabled(kCanaryExePath));
+ EXPECT_FALSE(AreUsageStatsEnabled(kChromeSystemExePath));
+ }
+}
+
+TEST_P(ChromeElfUtilTest, UsageStatsZeroInStateMedium) {
+ if (!system_level_)
+ return;
+ SetUsageStat(0, true);
+ EXPECT_FALSE(AreUsageStatsEnabled(chrome_path_));
+}
+
+TEST_P(ChromeElfUtilTest, UsageStatsOneInStateMedium) {
+ if (!system_level_)
+ return;
+ SetUsageStat(1, true);
+ EXPECT_TRUE(AreUsageStatsEnabled(chrome_path_));
+ EXPECT_FALSE(AreUsageStatsEnabled(kCanaryExePath));
+ EXPECT_FALSE(AreUsageStatsEnabled(kChromeUserExePath));
+}
+
+INSTANTIATE_TEST_CASE_P(Canary, ChromeElfUtilTest,
+ testing::Combine(testing::Values("canary"),
+ testing::Values("user"),
+ testing::Values("single")));
+INSTANTIATE_TEST_CASE_P(GoogleChrome, ChromeElfUtilTest,
+ testing::Combine(testing::Values("google"),
+ testing::Values("user", "system"),
+ testing::Values("single", "multi")));
+
+} // namespace
diff --git a/chrome_elf/create_file/chrome_create_file.cc b/chrome_elf/create_file/chrome_create_file.cc
index b6744f3..6bb2c78 100644
--- a/chrome_elf/create_file/chrome_create_file.cc
+++ b/chrome_elf/create_file/chrome_create_file.cc
@@ -8,6 +8,7 @@
#include "base/strings/string16.h"
#include "chrome_elf/chrome_elf_constants.h"
+#include "chrome_elf/chrome_elf_util.h"
#include "chrome_elf/ntdll_cache.h"
#include "sandbox/win/src/nt_internals.h"
@@ -243,11 +244,6 @@ HANDLE CreateFileNTDLL(
return file_handle;
}
-bool IsCanary(LPWSTR exe_path) {
- wchar_t* found = wcsstr(exe_path, L"Google\\Chrome SxS");
- return !!found;
-}
-
bool ShouldBypass(LPCWSTR file_path) {
// Do not redirect in non-browser processes.
wchar_t* command_line = ::GetCommandLine();
diff --git a/chrome_elf/create_file/chrome_create_file.h b/chrome_elf/create_file/chrome_create_file.h
index df38986..dac93af 100644
--- a/chrome_elf/create_file/chrome_create_file.h
+++ b/chrome_elf/create_file/chrome_create_file.h
@@ -35,8 +35,4 @@ HANDLE CreateFileNTDLL(
// system version (only uses ours if we're writing to the user data directory).
bool ShouldBypass(LPCWSTR file_name);
-// Returns true if |exe_path| points to a Chrome installed in a SxS
-// installation.
-bool IsCanary(LPWSTR exe_path);
-
#endif // CHROME_ELF_CREATE_FILE_CHROME_CREATE_FILE_H_
diff --git a/chrome_elf/create_file/chrome_create_file_unittest.cc b/chrome_elf/create_file/chrome_create_file_unittest.cc
index 1d5800a..e25b159 100644
--- a/chrome_elf/create_file/chrome_create_file_unittest.cc
+++ b/chrome_elf/create_file/chrome_create_file_unittest.cc
@@ -343,12 +343,6 @@ TEST_F(ChromeCreateFileTest, CheckParams_FILE_FLAG_OPEN_NO_RECALL) {
ResetNtCreateFileCalls();
}
-TEST_F(ChromeCreateFileTest, CanaryTest) {
- EXPECT_TRUE(IsCanary(L"C:\\Users\\user\\AppData\\Local\\Google\\Chrome SxS"));
- EXPECT_FALSE(IsCanary(L"C:\\Users\\user\\AppData\\Local\\Google\\Chrome"));
- EXPECT_FALSE(IsCanary(L"C:\\Users\\user\\AppData\\Local\\Chromium"));
-}
-
TEST_F(ChromeCreateFileTest, BypassTest) {
std::wstring UNC_filepath_file(L"\\\\.\\some_file.txt");