1 // Copyright 2013 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 "components/crash/content/app/breakpad_win.h"
6
7 #include <crtdbg.h>
8 #include <intrin.h>
9 #include <shellapi.h>
10 #include <stddef.h>
11 #include <tchar.h>
12 #include <userenv.h>
13 #include <windows.h>
14 #include <winnt.h>
15
16 #include <algorithm>
17 #include <map>
18 #include <memory>
19 #include <vector>
20
21 #include "base/base_switches.h"
22 #include "base/command_line.h"
23 #include "base/debug/crash_logging.h"
24 #include "base/debug/dump_without_crashing.h"
25 #include "base/environment.h"
26 #include "base/files/file_path.h"
27 #include "base/no_destructor.h"
28 #include "base/numerics/safe_conversions.h"
29 #include "base/stl_util.h"
30 #include "base/strings/string16.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/strings/string_split.h"
33 #include "base/strings/string_util.h"
34 #include "base/strings/stringprintf.h"
35 #include "base/strings/utf_string_conversions.h"
36 #include "base/win/pe_image.h"
37 #include "base/win/win_util.h"
38 #include "components/crash/content/app/hard_error_handler_win.h"
39 #include "components/crash/core/app/crash_reporter_client.h"
40 #include "components/crash/core/common/crash_keys.h"
41 #include "content/public/common/result_codes.h"
42 #include "sandbox/win/src/nt_internals.h"
43 #include "sandbox/win/src/sidestep/preamble_patcher.h"
44 #include "third_party/breakpad/breakpad/src/client/windows/common/ipc_protocol.h"
45 #include "third_party/breakpad/breakpad/src/client/windows/handler/exception_handler.h"
46
47 #pragma intrinsic(_AddressOfReturnAddress)
48 #pragma intrinsic(_ReturnAddress)
49
50 #ifdef _WIN64
51 // See http://msdn.microsoft.com/en-us/library/ddssxxy8.aspx
52 typedef struct _UNWIND_INFO {
53 unsigned char Version : 3;
54 unsigned char Flags : 5;
55 unsigned char SizeOfProlog;
56 unsigned char CountOfCodes;
57 unsigned char FrameRegister : 4;
58 unsigned char FrameOffset : 4;
59 ULONG ExceptionHandler;
60 } UNWIND_INFO, *PUNWIND_INFO;
61 #endif
62
63 namespace breakpad {
64
65 using crash_reporter::GetCrashReporterClient;
66
67 namespace {
68
69 // Minidump with stacks, PEB, TEB, and unloaded module list.
70 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
71 MiniDumpWithProcessThreadData | // Get PEB and TEB.
72 MiniDumpWithUnloadedModules); // Get unloaded modules when available.
73
74 // Minidump with all of the above, plus memory referenced from stack.
75 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
76 MiniDumpWithProcessThreadData | // Get PEB and TEB.
77 MiniDumpWithUnloadedModules | // Get unloaded modules when available.
78 MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
79
80 // Large dump with all process memory.
81 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
82 MiniDumpWithFullMemory | // Full memory from process.
83 MiniDumpWithProcessThreadData | // Get PEB and TEB.
84 MiniDumpWithHandleData | // Get all handle information.
85 MiniDumpWithUnloadedModules); // Get unloaded modules when available.
86
87 const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME";
88
89 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
90 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
91
92 // This is the well known SID for the system principal.
93 const wchar_t kSystemPrincipalSid[] = L"S-1-5-18";
94
95 google_breakpad::ExceptionHandler* g_breakpad = nullptr;
96 google_breakpad::ExceptionHandler* g_dumphandler_no_crash = nullptr;
97
98 #if !defined(_WIN64)
99 EXCEPTION_POINTERS g_surrogate_exception_pointers = {0};
100 EXCEPTION_RECORD g_surrogate_exception_record = {0};
101 CONTEXT g_surrogate_context = {0};
102 #endif // !defined(_WIN64)
103
104 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle,
105 NTSTATUS ExitStatus);
106 char* g_real_terminate_process_stub = nullptr;
107
108 // Returns the custom info structure based on the dll in parameter and the
109 // process type.
GetCustomInfo(const std::wstring & exe_path,const std::wstring & type,const std::wstring & profile_type,base::CommandLine * cmd_line,crash_reporter::CrashReporterClient * crash_client)110 google_breakpad::CustomClientInfo* GetCustomInfo(
111 const std::wstring& exe_path,
112 const std::wstring& type,
113 const std::wstring& profile_type,
114 base::CommandLine* cmd_line,
115 crash_reporter::CrashReporterClient* crash_client) {
116 base::string16 version, product, special_build, channel_name;
117 crash_client->GetProductNameAndVersion(exe_path, &product, &version,
118 &special_build, &channel_name);
119
120 // We only expect this method to be called once per process.
121 // Common enties
122 static base::NoDestructor<std::vector<google_breakpad::CustomInfoEntry>>
123 custom_entries;
124 custom_entries->push_back(
125 google_breakpad::CustomInfoEntry(L"ver", version.c_str()));
126 custom_entries->push_back(
127 google_breakpad::CustomInfoEntry(L"prod", product.c_str()));
128 custom_entries->push_back(
129 google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
130 custom_entries->push_back(
131 google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
132 custom_entries->push_back(google_breakpad::CustomInfoEntry(
133 L"pid", base::NumberToString16(::GetCurrentProcessId()).c_str()));
134 custom_entries->push_back(
135 google_breakpad::CustomInfoEntry(L"channel", channel_name.c_str()));
136 custom_entries->push_back(
137 google_breakpad::CustomInfoEntry(L"profile-type", profile_type.c_str()));
138
139 if (!special_build.empty()) {
140 custom_entries->push_back(
141 google_breakpad::CustomInfoEntry(L"special", special_build.c_str()));
142 }
143
144 // Check whether configuration management controls crash reporting.
145 bool crash_reporting_enabled = true;
146 bool controlled_by_policy =
147 crash_client->ReportingIsEnforcedByPolicy(&crash_reporting_enabled);
148 bool use_crash_service = !controlled_by_policy &&
149 (cmd_line->HasSwitch(switches::kNoErrorDialogs) ||
150 crash_client->IsRunningUnattended());
151 if (use_crash_service) {
152 base::string16 crash_dumps_dir_path;
153 if (crash_client->GetAlternativeCrashDumpLocation(&crash_dumps_dir_path)) {
154 custom_entries->push_back(google_breakpad::CustomInfoEntry(
155 L"breakpad-dump-location", crash_dumps_dir_path.c_str()));
156 }
157 }
158
159 static base::NoDestructor<google_breakpad::CustomClientInfo>
160 custom_client_info;
161 custom_client_info->entries = &custom_entries->front();
162 custom_client_info->count = custom_entries->size();
163
164 return custom_client_info.get();
165 }
166
167 } // namespace
168
169 // Dumps the current process memory.
DumpProcess()170 extern "C" void __declspec(dllexport) __cdecl DumpProcess() {
171 if (g_breakpad) {
172 g_breakpad->WriteMinidump();
173 }
174 }
175
176 // Used for dumping a process state when there is no crash.
DumpProcessWithoutCrash()177 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
178 if (g_dumphandler_no_crash) {
179 g_dumphandler_no_crash->WriteMinidump();
180 }
181 }
182
183 namespace {
184
DumpProcessWithoutCrashThread(void *)185 DWORD WINAPI DumpProcessWithoutCrashThread(void*) {
186 DumpProcessWithoutCrash();
187 return 0;
188 }
189
190 } // namespace
191
InjectDumpForHungInput(HANDLE process)192 extern "C" HANDLE __declspec(dllexport) __cdecl InjectDumpForHungInput(
193 HANDLE process) {
194 // |serialized_crash_keys| is not propagated in breakpad but is in crashpad
195 // since breakpad is deprecated.
196 return CreateRemoteThread(process, nullptr, 0, DumpProcessWithoutCrashThread,
197 nullptr, 0, nullptr);
198 }
199
200 // Returns a string containing a list of all modifiers for the loaded profile.
GetProfileType()201 std::wstring GetProfileType() {
202 std::wstring profile_type;
203 DWORD profile_bits = 0;
204 if (::GetProfileType(&profile_bits)) {
205 static const struct {
206 DWORD bit;
207 const wchar_t* name;
208 } kBitNames[] = {
209 { PT_MANDATORY, L"mandatory" },
210 { PT_ROAMING, L"roaming" },
211 { PT_TEMPORARY, L"temporary" },
212 };
213 for (size_t i = 0; i < base::size(kBitNames); ++i) {
214 const DWORD this_bit = kBitNames[i].bit;
215 if ((profile_bits & this_bit) != 0) {
216 profile_type.append(kBitNames[i].name);
217 profile_bits &= ~this_bit;
218 if (profile_bits != 0)
219 profile_type.append(L", ");
220 }
221 }
222 } else {
223 DWORD last_error = ::GetLastError();
224 base::SStringPrintf(&profile_type, L"error %u", last_error);
225 }
226 return profile_type;
227 }
228
229 namespace {
230
231 // This callback is used when we want to get a dump without crashing the
232 // process.
DumpDoneCallbackWhenNoCrash(const wchar_t *,const wchar_t *,void *,EXCEPTION_POINTERS * ex_info,MDRawAssertionInfo *,bool succeeded)233 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*,
234 EXCEPTION_POINTERS* ex_info,
235 MDRawAssertionInfo*, bool succeeded) {
236 return true;
237 }
238
239 // This callback is executed when the browser process has crashed, after
240 // the crash dump has been created. We need to minimize the amount of work
241 // done here since we have potentially corrupted process. Our job is to
242 // spawn another instance of chrome which will show a 'chrome has crashed'
243 // dialog. This code needs to live in the exe and thus has no access to
244 // facilities such as the i18n helpers.
DumpDoneCallback(const wchar_t *,const wchar_t *,void *,EXCEPTION_POINTERS * ex_info,MDRawAssertionInfo *,bool succeeded)245 bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*,
246 EXCEPTION_POINTERS* ex_info,
247 MDRawAssertionInfo*, bool succeeded) {
248 // Check if the exception is one of the kind which would not be solved
249 // by simply restarting chrome. In this case we show a message box with
250 // and exit silently. Remember that chrome is in a crashed state so we
251 // can't show our own UI from this process.
252 if (HardErrorHandler(ex_info))
253 return true;
254
255 if (!GetCrashReporterClient()->AboutToRestart())
256 return true;
257
258 // Now we just start chrome browser with the same command line.
259 STARTUPINFOW si = {sizeof(si)};
260 PROCESS_INFORMATION pi;
261 if (::CreateProcessW(nullptr, ::GetCommandLineW(), nullptr, nullptr, FALSE,
262 CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, &si,
263 &pi)) {
264 ::CloseHandle(pi.hProcess);
265 ::CloseHandle(pi.hThread);
266 }
267 // After this return we will be terminated. The actual return value is
268 // not used at all.
269 return true;
270 }
271
272 // flag to indicate that we are already handling an exception.
273 volatile LONG handling_exception = 0;
274
275 // This callback is used when there is no crash. Note: Unlike the
276 // |FilterCallback| below this does not do dupe detection. It is upto the caller
277 // to implement it.
FilterCallbackWhenNoCrash(void *,EXCEPTION_POINTERS *,MDRawAssertionInfo *)278 bool FilterCallbackWhenNoCrash(
279 void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
280 return true;
281 }
282
283 // This callback is executed when the Chrome process has crashed and *before*
284 // the crash dump is created. To prevent duplicate crash reports we
285 // make every thread calling this method, except the very first one,
286 // go to sleep.
FilterCallback(void *,EXCEPTION_POINTERS *,MDRawAssertionInfo *)287 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
288 // Capture every thread except the first one in the sleep. We don't
289 // want multiple threads to concurrently report exceptions.
290 if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) {
291 ::Sleep(INFINITE);
292 }
293 return true;
294 }
295
296 // Previous unhandled filter. Will be called if not null when we
297 // intercept a crash.
298 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = nullptr;
299
300 // Exception filter used when breakpad is not enabled. We just display
301 // the "Do you want to restart" message and then we call the previous filter.
ChromeExceptionFilter(EXCEPTION_POINTERS * info)302 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) {
303 DumpDoneCallback(nullptr, nullptr, nullptr, info, nullptr, false);
304
305 if (previous_filter)
306 return previous_filter(info);
307
308 return EXCEPTION_EXECUTE_HANDLER;
309 }
310
311 // Exception filter for the Cloud Print service process used when breakpad is
312 // not enabled. We just display the "Do you want to restart" message and then
313 // die (without calling the previous filter).
CloudPrintServiceExceptionFilter(EXCEPTION_POINTERS * info)314 long WINAPI CloudPrintServiceExceptionFilter(EXCEPTION_POINTERS* info) {
315 DumpDoneCallback(nullptr, nullptr, nullptr, info, nullptr, false);
316 return EXCEPTION_EXECUTE_HANDLER;
317 }
318
319 } // namespace
320
WrapMessageBoxWithSEH(const wchar_t * text,const wchar_t * caption,UINT flags,bool * exit_now)321 static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption,
322 UINT flags, bool* exit_now) {
323 // We wrap the call to MessageBoxW with a SEH handler because it some
324 // machines with CursorXP, PeaDict or with FontExplorer installed it crashes
325 // uncontrollably here. Being this a best effort deal we better go away.
326 __try {
327 *exit_now = (IDOK != ::MessageBoxW(nullptr, text, caption, flags));
328 } __except(EXCEPTION_EXECUTE_HANDLER) {
329 // Its not safe to continue executing, exit silently here.
330 ::TerminateProcess(::GetCurrentProcess(),
331 GetCrashReporterClient()->GetResultCodeRespawnFailed());
332 }
333
334 return true;
335 }
336
337 // This function is executed by the child process that DumpDoneCallback()
338 // spawned and basically just shows the 'chrome has crashed' dialog if
339 // the CHROME_CRASHED environment variable is present.
ShowRestartDialogIfCrashed(bool * exit_now)340 bool ShowRestartDialogIfCrashed(bool* exit_now) {
341 base::string16 message;
342 base::string16 title;
343 bool is_rtl_locale;
344 if (!GetCrashReporterClient()->ShouldShowRestartDialog(
345 &title, &message, &is_rtl_locale)) {
346 return false;
347 }
348
349 // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX
350 // flags so that an RTL message box is displayed.
351 UINT flags = MB_OKCANCEL | MB_ICONWARNING;
352 if (is_rtl_locale)
353 flags |= MB_RIGHT | MB_RTLREADING;
354
355 return WrapMessageBoxWithSEH(message.c_str(), title.c_str(), flags, exit_now);
356 }
357
TerminateProcessWithoutDump()358 extern "C" void __declspec(dllexport) TerminateProcessWithoutDump() {
359 // Patched stub exists based on conditions (See InitCrashReporter).
360 // As a side note this function also gets called from
361 // WindowProcExceptionFilter.
362 if (g_real_terminate_process_stub == nullptr) {
363 ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
364 } else {
365 NtTerminateProcessPtr real_terminate_proc =
366 reinterpret_cast<NtTerminateProcessPtr>(
367 static_cast<char*>(g_real_terminate_process_stub));
368 real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
369 }
370 }
371
372 // Crashes the process after generating a dump for the provided exception. Note
373 // that the crash reporter should be initialized before calling this function
374 // for it to do anything.
CrashForException(EXCEPTION_POINTERS * info)375 extern "C" int __declspec(dllexport) CrashForException(
376 EXCEPTION_POINTERS* info) {
377 if (g_breakpad) {
378 g_breakpad->WriteMinidumpForException(info);
379 TerminateProcessWithoutDump();
380 }
381 return EXCEPTION_CONTINUE_SEARCH;
382 }
383
384 #ifndef _WIN64
HookNtTerminateProcess(HANDLE ProcessHandle,NTSTATUS ExitStatus)385 static NTSTATUS WINAPI HookNtTerminateProcess(HANDLE ProcessHandle,
386 NTSTATUS ExitStatus) {
387 if (g_breakpad &&
388 (ProcessHandle == ::GetCurrentProcess() || ProcessHandle == NULL)) {
389 NT_TIB* tib = reinterpret_cast<NT_TIB*>(NtCurrentTeb());
390 void* address_on_stack = _AddressOfReturnAddress();
391 if (address_on_stack < tib->StackLimit ||
392 address_on_stack > tib->StackBase) {
393 g_surrogate_exception_record.ExceptionAddress = _ReturnAddress();
394 g_surrogate_exception_record.ExceptionCode = DBG_TERMINATE_PROCESS;
395 g_surrogate_exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
396 CrashForException(&g_surrogate_exception_pointers);
397 }
398 }
399
400 NtTerminateProcessPtr real_proc =
401 reinterpret_cast<NtTerminateProcessPtr>(
402 static_cast<char*>(g_real_terminate_process_stub));
403 return real_proc(ProcessHandle, ExitStatus);
404 }
405
InitTerminateProcessHooks()406 static void InitTerminateProcessHooks() {
407 NtTerminateProcessPtr terminate_process_func_address =
408 reinterpret_cast<NtTerminateProcessPtr>(::GetProcAddress(
409 ::GetModuleHandle(L"ntdll.dll"), "NtTerminateProcess"));
410 if (terminate_process_func_address == NULL)
411 return;
412
413 DWORD old_protect = 0;
414 if (!::VirtualProtect(reinterpret_cast<void*>(terminate_process_func_address),
415 5, PAGE_EXECUTE_READWRITE, &old_protect))
416 return;
417
418 g_real_terminate_process_stub = reinterpret_cast<char*>(VirtualAllocEx(
419 ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize,
420 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
421 if (g_real_terminate_process_stub == NULL)
422 return;
423
424 g_surrogate_exception_pointers.ContextRecord = &g_surrogate_context;
425 g_surrogate_exception_pointers.ExceptionRecord =
426 &g_surrogate_exception_record;
427
428 sidestep::SideStepError patch_result = sidestep::PreamblePatcher::Patch(
429 reinterpret_cast<void*>(terminate_process_func_address),
430 reinterpret_cast<void*>(HookNtTerminateProcess),
431 g_real_terminate_process_stub, sidestep::kMaxPreambleStubSize);
432 if (patch_result != sidestep::SIDESTEP_SUCCESS) {
433 CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub,
434 0, MEM_RELEASE));
435 CHECK(::VirtualProtect(
436 reinterpret_cast<void*>(terminate_process_func_address), 5, old_protect,
437 &old_protect));
438 return;
439 }
440
441 DWORD dummy = 0;
442 CHECK(
443 ::VirtualProtect(reinterpret_cast<void*>(terminate_process_func_address),
444 5, old_protect, &dummy));
445 CHECK(::VirtualProtect(g_real_terminate_process_stub,
446 sidestep::kMaxPreambleStubSize,
447 old_protect,
448 &old_protect));
449 }
450 #endif
451
InitPipeNameEnvVar(bool is_per_user_install)452 static void InitPipeNameEnvVar(bool is_per_user_install) {
453 std::unique_ptr<base::Environment> env(base::Environment::Create());
454 if (env->HasVar(kPipeNameVar)) {
455 // The Breakpad pipe name is already configured: nothing to do.
456 return;
457 }
458
459 // Check whether configuration management controls crash reporting.
460 bool crash_reporting_enabled = true;
461 bool controlled_by_policy =
462 GetCrashReporterClient()->ReportingIsEnforcedByPolicy(
463 &crash_reporting_enabled);
464
465 const base::CommandLine& command = *base::CommandLine::ForCurrentProcess();
466 bool use_crash_service = !controlled_by_policy &&
467 (command.HasSwitch(switches::kNoErrorDialogs) ||
468 GetCrashReporterClient()->IsRunningUnattended());
469
470 std::wstring pipe_name;
471 if (use_crash_service) {
472 // Crash reporting is done by crash_service.exe.
473 pipe_name = kChromePipeName;
474 } else {
475 // We want to use the Google Update crash reporting. We need to check if the
476 // user allows it first (in case the administrator didn't already decide
477 // via policy).
478 if (!controlled_by_policy)
479 crash_reporting_enabled =
480 GetCrashReporterClient()->GetCollectStatsConsent();
481
482 if (!crash_reporting_enabled) {
483 // Crash reporting is disabled, don't set the environment variable.
484 return;
485 }
486
487 // Build the pipe name. It can be either:
488 // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
489 // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
490 std::wstring user_sid;
491 if (is_per_user_install) {
492 if (!base::win::GetUserSidString(&user_sid)) {
493 return;
494 }
495 } else {
496 user_sid = kSystemPrincipalSid;
497 }
498
499 pipe_name = kGoogleUpdatePipeName;
500 pipe_name += user_sid;
501 }
502 env->SetVar(kPipeNameVar, base::UTF16ToASCII(pipe_name));
503 }
504
InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter)505 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
506 previous_filter = SetUnhandledExceptionFilter(filter);
507 }
508
InitCrashReporter(const std::string & process_type_switch)509 void InitCrashReporter(const std::string& process_type_switch) {
510 const base::CommandLine& command = *base::CommandLine::ForCurrentProcess();
511 if (command.HasSwitch(switches::kDisableBreakpad))
512 return;
513
514 // Disable the message box for assertions.
515 _CrtSetReportMode(_CRT_ASSERT, 0);
516
517 base::string16 process_type = base::ASCIIToUTF16(process_type_switch);
518 if (process_type.empty())
519 process_type = L"browser";
520
521 wchar_t exe_path[MAX_PATH];
522 exe_path[0] = 0;
523 GetModuleFileNameW(nullptr, exe_path, MAX_PATH);
524
525 google_breakpad::CustomClientInfo* custom_info = GetCustomInfo(
526 exe_path, process_type, GetProfileType(),
527 base::CommandLine::ForCurrentProcess(), GetCrashReporterClient());
528
529 google_breakpad::ExceptionHandler::MinidumpCallback callback = nullptr;
530 LPTOP_LEVEL_EXCEPTION_FILTER default_filter = nullptr;
531 // We install the post-dump callback only for the browser and service
532 // processes. It spawns a new browser/service process.
533 if (process_type == L"browser") {
534 callback = &DumpDoneCallback;
535 default_filter = &ChromeExceptionFilter;
536 } else if (process_type == L"service") {
537 callback = &DumpDoneCallback;
538 default_filter = &CloudPrintServiceExceptionFilter;
539 }
540
541 if (GetCrashReporterClient()->ShouldCreatePipeName(process_type))
542 InitPipeNameEnvVar(GetCrashReporterClient()->GetIsPerUserInstall());
543
544 std::unique_ptr<base::Environment> env(base::Environment::Create());
545 std::string pipe_name_ascii;
546 if (!env->GetVar(kPipeNameVar, &pipe_name_ascii)) {
547 // Breakpad is not enabled. Configuration is managed or the user
548 // did not allow Google Update to send crashes. We need to use
549 // our default crash handler instead, but only for the
550 // browser/service processes.
551 if (default_filter)
552 InitDefaultCrashCallback(default_filter);
553 return;
554 }
555 base::string16 pipe_name = base::ASCIIToUTF16(pipe_name_ascii);
556
557 #ifdef _WIN64
558 // The protocol for connecting to the out-of-process Breakpad crash
559 // reporter is different for x86-32 and x86-64: the message sizes
560 // are different because the message struct contains a pointer. As
561 // a result, there are two different named pipes to connect to. The
562 // 64-bit one is distinguished with an "-x64" suffix.
563 pipe_name += L"-x64";
564 #endif
565
566 // Get the alternate dump directory. We use the temp path.
567 wchar_t temp_dir[MAX_PATH] = {0};
568 ::GetTempPathW(MAX_PATH, temp_dir);
569
570 MINIDUMP_TYPE dump_type = kSmallDumpType;
571 // Capture full memory if explicitly instructed to.
572 if (command.HasSwitch(switches::kFullMemoryCrashReport))
573 dump_type = kFullDumpType;
574 else if (GetCrashReporterClient()->GetShouldDumpLargerDumps())
575 dump_type = kLargerDumpType;
576
577 g_breakpad = new google_breakpad::ExceptionHandler(
578 temp_dir, &FilterCallback, callback, nullptr,
579 google_breakpad::ExceptionHandler::HANDLER_ALL, dump_type,
580 pipe_name.c_str(), custom_info);
581
582 // Now initialize the non crash dump handler.
583 g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(
584 temp_dir, &FilterCallbackWhenNoCrash, &DumpDoneCallbackWhenNoCrash,
585 nullptr,
586 // Set the handler to none so this handler would not be added to
587 // |handler_stack_| in |ExceptionHandler| which is a list of exception
588 // handlers.
589 google_breakpad::ExceptionHandler::HANDLER_NONE, dump_type,
590 pipe_name.c_str(), custom_info);
591
592 // Set the DumpWithoutCrashingFunction for this instance of base.lib. Other
593 // executable images linked with base should set this again for
594 // DumpWithoutCrashing to function correctly.
595 // See chrome_main.cc for example.
596 base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash);
597
598 if (g_breakpad->IsOutOfProcess()) {
599 // Tells breakpad to handle breakpoint and single step exceptions.
600 // This might break JIT debuggers, but at least it will always
601 // generate a crashdump for these exceptions.
602 g_breakpad->set_handle_debug_exceptions(true);
603
604 #ifndef _WIN64
605 if (process_type != L"browser" &&
606 !GetCrashReporterClient()->IsRunningUnattended()) {
607 // Initialize the hook TerminateProcess to catch unexpected exits.
608 InitTerminateProcessHooks();
609 }
610 #endif
611 }
612 }
613
ConsumeInvalidHandleExceptions()614 void ConsumeInvalidHandleExceptions() {
615 if (g_breakpad) {
616 g_breakpad->set_consume_invalid_handle_exceptions(true);
617 }
618 if (g_dumphandler_no_crash) {
619 g_dumphandler_no_crash->set_consume_invalid_handle_exceptions(true);
620 }
621 }
622
623 // If the user has disabled crash reporting uploads and restarted Chrome, the
624 // restarted instance will still contain the pipe environment variable, which
625 // will allow the restarted process to still upload crash reports. This function
626 // clears the environment variable, so that the restarted Chrome, which inherits
627 // its environment from the current Chrome, will no longer contain the variable.
628 extern "C" void __declspec(dllexport) __cdecl
ClearBreakpadPipeEnvironmentVariable()629 ClearBreakpadPipeEnvironmentVariable() {
630 std::unique_ptr<base::Environment> env(base::Environment::Create());
631 env->UnSetVar(kPipeNameVar);
632 }
633
634 } // namespace breakpad
635