Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / components / breakpad / app / breakpad_win.cc
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/breakpad/app/breakpad_win.h"
6
7 #include <windows.h>
8 #include <shellapi.h>
9 #include <tchar.h>
10 #include <userenv.h>
11 #include <winnt.h>
12
13 #include <algorithm>
14 #include <vector>
15
16 #include "base/base_switches.h"
17 #include "base/basictypes.h"
18 #include "base/command_line.h"
19 #include "base/debug/crash_logging.h"
20 #include "base/debug/dump_without_crashing.h"
21 #include "base/environment.h"
22 #include "base/memory/scoped_ptr.h"
23 #include "base/strings/string16.h"
24 #include "base/strings/string_split.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/synchronization/lock.h"
29 #include "base/win/metro.h"
30 #include "base/win/pe_image.h"
31 #include "base/win/registry.h"
32 #include "base/win/win_util.h"
33 #include "breakpad/src/client/windows/handler/exception_handler.h"
34 #include "components/breakpad/app/breakpad_client.h"
35 #include "components/breakpad/app/hard_error_handler_win.h"
36 #include "content/public/common/result_codes.h"
37 #include "sandbox/win/src/nt_internals.h"
38 #include "sandbox/win/src/sidestep/preamble_patcher.h"
39
40 // userenv.dll is required for GetProfileType().
41 #pragma comment(lib, "userenv.lib")
42
43 #pragma intrinsic(_AddressOfReturnAddress)
44 #pragma intrinsic(_ReturnAddress)
45
46 namespace breakpad {
47
48 std::vector<google_breakpad::CustomInfoEntry>* g_custom_entries = NULL;
49 bool g_deferred_crash_uploads = false;
50
51 namespace {
52
53 // Minidump with stacks, PEB, TEB, and unloaded module list.
54 const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
55     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
56     MiniDumpWithUnloadedModules);  // Get unloaded modules when available.
57
58 // Minidump with all of the above, plus memory referenced from stack.
59 const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
60     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
61     MiniDumpWithUnloadedModules |  // Get unloaded modules when available.
62     MiniDumpWithIndirectlyReferencedMemory);  // Get memory referenced by stack.
63
64 // Large dump with all process memory.
65 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
66     MiniDumpWithFullMemory |  // Full memory from process.
67     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
68     MiniDumpWithHandleData |  // Get all handle information.
69     MiniDumpWithUnloadedModules);  // Get unloaded modules when available.
70
71 const char kPipeNameVar[] = "CHROME_BREAKPAD_PIPE_NAME";
72
73 const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\";
74 const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices";
75
76 // This is the well known SID for the system principal.
77 const wchar_t kSystemPrincipalSid[] =L"S-1-5-18";
78
79 google_breakpad::ExceptionHandler* g_breakpad = NULL;
80 google_breakpad::ExceptionHandler* g_dumphandler_no_crash = NULL;
81
82 EXCEPTION_POINTERS g_surrogate_exception_pointers = {0};
83 EXCEPTION_RECORD g_surrogate_exception_record = {0};
84 CONTEXT g_surrogate_context = {0};
85
86 typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle,
87                                                  NTSTATUS ExitStatus);
88 char* g_real_terminate_process_stub = NULL;
89
90 base::Lock* g_dynamic_entries_lock = NULL;
91 // Under *g_dynamic_entries_lock.
92 size_t g_dynamic_keys_offset = 0;
93 typedef std::map<std::wstring, google_breakpad::CustomInfoEntry*>
94     DynamicEntriesMap;
95 // Under *g_dynamic_entries_lock.
96 DynamicEntriesMap* g_dynamic_entries = NULL;
97
98 // Allow for 256 dynamic entries in addition to the fixed set of entries
99 // set up in this file.
100 const size_t kMaxDynamicEntries = 256;
101
102 // Maximum length for plugin path to include in plugin crash reports.
103 const size_t kMaxPluginPathLength = 256;
104
105 // Dumps the current process memory.
106 extern "C" void __declspec(dllexport) __cdecl DumpProcess() {
107   if (g_breakpad) {
108     g_breakpad->WriteMinidump();
109   }
110 }
111
112 // Used for dumping a process state when there is no crash.
113 extern "C" void __declspec(dllexport) __cdecl DumpProcessWithoutCrash() {
114   if (g_dumphandler_no_crash) {
115     g_dumphandler_no_crash->WriteMinidump();
116   }
117 }
118
119 // We need to prevent ICF from folding DumpForHangDebuggingThread() and
120 // DumpProcessWithoutCrashThread() together, since that makes them
121 // indistinguishable in crash dumps. We do this by making the function
122 // bodies unique, and prevent optimization from shuffling things around.
123 MSVC_DISABLE_OPTIMIZE()
124 MSVC_PUSH_DISABLE_WARNING(4748)
125
126 DWORD WINAPI DumpProcessWithoutCrashThread(void*) {
127   DumpProcessWithoutCrash();
128   return 0;
129 }
130
131 // The following two functions do exactly the same thing as the two above. But
132 // we want the signatures to be different so that we can easily track them in
133 // crash reports.
134 // TODO(yzshen): Remove when enough information is collected and the hang rate
135 // of pepper/renderer processes is reduced.
136 DWORD WINAPI DumpForHangDebuggingThread(void*) {
137   DumpProcessWithoutCrash();
138   VLOG(1) << "dumped for hang debugging";
139   return 0;
140 }
141
142 MSVC_POP_WARNING()
143 MSVC_ENABLE_OPTIMIZE()
144
145 // Injects a thread into a remote process to dump state when there is no crash.
146 extern "C" HANDLE __declspec(dllexport) __cdecl
147 InjectDumpProcessWithoutCrash(HANDLE process) {
148   return CreateRemoteThread(process, NULL, 0, DumpProcessWithoutCrashThread,
149                             0, 0, NULL);
150 }
151
152 extern "C" HANDLE __declspec(dllexport) __cdecl
153 InjectDumpForHangDebugging(HANDLE process) {
154   return CreateRemoteThread(process, NULL, 0, DumpForHangDebuggingThread,
155                             0, 0, NULL);
156 }
157
158 // Appends the plugin path to |g_custom_entries|.
159 void SetPluginPath(const std::wstring& path) {
160   DCHECK(g_custom_entries);
161
162   if (path.size() > kMaxPluginPathLength) {
163     // If the path is too long, truncate from the start rather than the end,
164     // since we want to be able to recover the DLL name.
165     SetPluginPath(path.substr(path.size() - kMaxPluginPathLength));
166     return;
167   }
168
169   // The chunk size without terminator.
170   const size_t kChunkSize = static_cast<size_t>(
171       google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
172
173   int chunk_index = 0;
174   size_t chunk_start = 0;  // Current position inside |path|
175
176   for (chunk_start = 0; chunk_start < path.size(); chunk_index++) {
177     size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start);
178
179     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
180         base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(),
181         path.substr(chunk_start, chunk_length).c_str()));
182
183     chunk_start += chunk_length;
184   }
185 }
186
187 // Appends the breakpad dump path to |g_custom_entries|.
188 void SetBreakpadDumpPath() {
189   DCHECK(g_custom_entries);
190   base::FilePath crash_dumps_dir_path;
191   if (GetBreakpadClient()->GetAlternativeCrashDumpLocation(
192           &crash_dumps_dir_path)) {
193     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
194         L"breakpad-dump-location", crash_dumps_dir_path.value().c_str()));
195   }
196 }
197
198 // Returns a string containing a list of all modifiers for the loaded profile.
199 std::wstring GetProfileType() {
200   std::wstring profile_type;
201   DWORD profile_bits = 0;
202   if (::GetProfileType(&profile_bits)) {
203     static const struct {
204       DWORD bit;
205       const wchar_t* name;
206     } kBitNames[] = {
207       { PT_MANDATORY, L"mandatory" },
208       { PT_ROAMING, L"roaming" },
209       { PT_TEMPORARY, L"temporary" },
210     };
211     for (size_t i = 0; i < arraysize(kBitNames); ++i) {
212       const DWORD this_bit = kBitNames[i].bit;
213       if ((profile_bits & this_bit) != 0) {
214         profile_type.append(kBitNames[i].name);
215         profile_bits &= ~this_bit;
216         if (profile_bits != 0)
217           profile_type.append(L", ");
218       }
219     }
220   } else {
221     DWORD last_error = ::GetLastError();
222     base::SStringPrintf(&profile_type, L"error %u", last_error);
223   }
224   return profile_type;
225 }
226
227 // Returns the custom info structure based on the dll in parameter and the
228 // process type.
229 google_breakpad::CustomClientInfo* GetCustomInfo(const std::wstring& exe_path,
230                                                  const std::wstring& type) {
231   base::string16 version, product;
232   base::string16 special_build;
233   base::string16 channel_name;
234   GetBreakpadClient()->GetProductNameAndVersion(
235       base::FilePath(exe_path),
236       &product,
237       &version,
238       &special_build,
239       &channel_name);
240
241   // We only expect this method to be called once per process.
242   DCHECK(!g_custom_entries);
243   g_custom_entries = new std::vector<google_breakpad::CustomInfoEntry>;
244
245   // Common g_custom_entries.
246   g_custom_entries->push_back(
247       google_breakpad::CustomInfoEntry(L"ver",
248                                        base::UTF16ToWide(version).c_str()));
249   g_custom_entries->push_back(
250       google_breakpad::CustomInfoEntry(L"prod",
251                                        base::UTF16ToWide(product).c_str()));
252   g_custom_entries->push_back(
253       google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
254   g_custom_entries->push_back(
255       google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
256   g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
257       L"pid", base::StringPrintf(L"%d", ::GetCurrentProcessId()).c_str()));
258   g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
259       L"channel", base::UTF16ToWide(channel_name).c_str()));
260   g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
261       L"profile-type", GetProfileType().c_str()));
262
263   if (g_deferred_crash_uploads)
264     g_custom_entries->push_back(
265         google_breakpad::CustomInfoEntry(L"deferred-upload", L"true"));
266
267   if (!special_build.empty())
268     g_custom_entries->push_back(google_breakpad::CustomInfoEntry(
269         L"special", base::UTF16ToWide(special_build).c_str()));
270
271   if (type == L"plugin" || type == L"ppapi") {
272     std::wstring plugin_path =
273         CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path");
274     if (!plugin_path.empty())
275       SetPluginPath(plugin_path);
276   }
277
278   // Check whether configuration management controls crash reporting.
279   bool crash_reporting_enabled = true;
280   bool controlled_by_policy = GetBreakpadClient()->ReportingIsEnforcedByPolicy(
281       &crash_reporting_enabled);
282   const CommandLine& command = *CommandLine::ForCurrentProcess();
283   bool use_crash_service =
284       !controlled_by_policy && (command.HasSwitch(switches::kNoErrorDialogs) ||
285                                 GetBreakpadClient()->IsRunningUnattended());
286   if (use_crash_service)
287     SetBreakpadDumpPath();
288
289   // Create space for dynamic ad-hoc keys. The names and values are set using
290   // the API defined in base/debug/crash_logging.h.
291   g_dynamic_keys_offset = g_custom_entries->size();
292   for (size_t i = 0; i < kMaxDynamicEntries; ++i) {
293     // The names will be mutated as they are set. Un-numbered since these are
294     // merely placeholders. The name cannot be empty because Breakpad's
295     // HTTPUpload will interpret that as an invalid parameter.
296     g_custom_entries->push_back(
297         google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L""));
298   }
299
300   g_dynamic_entries_lock = new base::Lock;
301   g_dynamic_entries = new DynamicEntriesMap;
302
303   static google_breakpad::CustomClientInfo custom_client_info;
304   custom_client_info.entries = &g_custom_entries->front();
305   custom_client_info.count = g_custom_entries->size();
306
307   return &custom_client_info;
308 }
309
310 // This callback is used when we want to get a dump without crashing the
311 // process.
312 bool DumpDoneCallbackWhenNoCrash(const wchar_t*, const wchar_t*, void*,
313                                  EXCEPTION_POINTERS* ex_info,
314                                  MDRawAssertionInfo*, bool) {
315   return true;
316 }
317
318 // This callback is executed when the browser process has crashed, after
319 // the crash dump has been created. We need to minimize the amount of work
320 // done here since we have potentially corrupted process. Our job is to
321 // spawn another instance of chrome which will show a 'chrome has crashed'
322 // dialog. This code needs to live in the exe and thus has no access to
323 // facilities such as the i18n helpers.
324 bool DumpDoneCallback(const wchar_t*, const wchar_t*, void*,
325                       EXCEPTION_POINTERS* ex_info,
326                       MDRawAssertionInfo*, bool) {
327   // Check if the exception is one of the kind which would not be solved
328   // by simply restarting chrome. In this case we show a message box with
329   // and exit silently. Remember that chrome is in a crashed state so we
330   // can't show our own UI from this process.
331   if (HardErrorHandler(ex_info))
332     return true;
333
334   if (!GetBreakpadClient()->AboutToRestart())
335     return true;
336
337   // Now we just start chrome browser with the same command line.
338   STARTUPINFOW si = {sizeof(si)};
339   PROCESS_INFORMATION pi;
340   if (::CreateProcessW(NULL, ::GetCommandLineW(), NULL, NULL, FALSE,
341                        CREATE_UNICODE_ENVIRONMENT, NULL, NULL, &si, &pi)) {
342     ::CloseHandle(pi.hProcess);
343     ::CloseHandle(pi.hThread);
344   }
345   // After this return we will be terminated. The actual return value is
346   // not used at all.
347   return true;
348 }
349
350 // flag to indicate that we are already handling an exception.
351 volatile LONG handling_exception = 0;
352
353 // This callback is used when there is no crash. Note: Unlike the
354 // |FilterCallback| below this does not do dupe detection. It is upto the caller
355 // to implement it.
356 bool FilterCallbackWhenNoCrash(
357     void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
358   GetBreakpadClient()->RecordCrashDumpAttempt(false);
359   return true;
360 }
361
362 // This callback is executed when the Chrome process has crashed and *before*
363 // the crash dump is created. To prevent duplicate crash reports we
364 // make every thread calling this method, except the very first one,
365 // go to sleep.
366 bool FilterCallback(void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*) {
367   // Capture every thread except the first one in the sleep. We don't
368   // want multiple threads to concurrently report exceptions.
369   if (::InterlockedCompareExchange(&handling_exception, 1, 0) == 1) {
370     ::Sleep(INFINITE);
371   }
372   GetBreakpadClient()->RecordCrashDumpAttempt(true);
373   return true;
374 }
375
376 // Previous unhandled filter. Will be called if not null when we
377 // intercept a crash.
378 LPTOP_LEVEL_EXCEPTION_FILTER previous_filter = NULL;
379
380 // Exception filter used when breakpad is not enabled. We just display
381 // the "Do you want to restart" message and then we call the previous filter.
382 long WINAPI ChromeExceptionFilter(EXCEPTION_POINTERS* info) {
383   DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
384
385   if (previous_filter)
386     return previous_filter(info);
387
388   return EXCEPTION_EXECUTE_HANDLER;
389 }
390
391 // Exception filter for the service process used when breakpad is not enabled.
392 // We just display the "Do you want to restart" message and then die
393 // (without calling the previous filter).
394 long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) {
395   DumpDoneCallback(NULL, NULL, NULL, info, NULL, false);
396   return EXCEPTION_EXECUTE_HANDLER;
397 }
398
399 // NOTE: This function is used by SyzyASAN to annotate crash reports. If you
400 // change the name or signature of this function you will break SyzyASAN
401 // instrumented releases of Chrome. Please contact syzygy-team@chromium.org
402 // before doing so!
403 extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl(
404     const wchar_t* key, const wchar_t* value) {
405   if (!g_dynamic_entries)
406     return;
407
408   // CustomInfoEntry limits the length of key and value. If they exceed
409   // their maximum length the underlying string handling functions raise
410   // an exception and prematurely trigger a crash. Truncate here.
411   std::wstring safe_key(std::wstring(key).substr(
412       0, google_breakpad::CustomInfoEntry::kNameMaxLength  - 1));
413   std::wstring safe_value(std::wstring(value).substr(
414       0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
415
416   // If we already have a value for this key, update it; otherwise, insert
417   // the new value if we have not exhausted the pre-allocated slots for dynamic
418   // entries.
419   DCHECK(g_dynamic_entries_lock);
420   base::AutoLock lock(*g_dynamic_entries_lock);
421
422   DynamicEntriesMap::iterator it = g_dynamic_entries->find(safe_key);
423   google_breakpad::CustomInfoEntry* entry = NULL;
424   if (it == g_dynamic_entries->end()) {
425     if (g_dynamic_entries->size() >= kMaxDynamicEntries)
426       return;
427     entry = &(*g_custom_entries)[g_dynamic_keys_offset++];
428     g_dynamic_entries->insert(std::make_pair(safe_key, entry));
429   } else {
430     entry = it->second;
431   }
432
433   entry->set(safe_key.data(), safe_value.data());
434 }
435
436 extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl(
437     const wchar_t* key) {
438   if (!g_dynamic_entries)
439     return;
440
441   DCHECK(g_dynamic_entries_lock);
442   base::AutoLock lock(*g_dynamic_entries_lock);
443
444   std::wstring key_string(key);
445   DynamicEntriesMap::iterator it = g_dynamic_entries->find(key_string);
446   if (it == g_dynamic_entries->end())
447     return;
448
449   it->second->set_value(NULL);
450 }
451
452 }  // namespace
453
454 static bool WrapMessageBoxWithSEH(const wchar_t* text, const wchar_t* caption,
455                                   UINT flags, bool* exit_now) {
456   // We wrap the call to MessageBoxW with a SEH handler because it some
457   // machines with CursorXP, PeaDict or with FontExplorer installed it crashes
458   // uncontrollably here. Being this a best effort deal we better go away.
459   __try {
460     *exit_now = (IDOK != ::MessageBoxW(NULL, text, caption, flags));
461   } __except(EXCEPTION_EXECUTE_HANDLER) {
462     // Its not safe to continue executing, exit silently here.
463     ::TerminateProcess(::GetCurrentProcess(),
464                        GetBreakpadClient()->GetResultCodeRespawnFailed());
465   }
466
467   return true;
468 }
469
470 // This function is executed by the child process that DumpDoneCallback()
471 // spawned and basically just shows the 'chrome has crashed' dialog if
472 // the CHROME_CRASHED environment variable is present.
473 bool ShowRestartDialogIfCrashed(bool* exit_now) {
474   // If we are being launched in metro mode don't try to show the dialog.
475   if (base::win::IsMetroProcess())
476     return false;
477
478   base::string16 message;
479   base::string16 title;
480   bool is_rtl_locale;
481   if (!GetBreakpadClient()->ShouldShowRestartDialog(
482            &title, &message, &is_rtl_locale)) {
483     return false;
484   }
485
486   // If the UI layout is right-to-left, we need to pass the appropriate MB_XXX
487   // flags so that an RTL message box is displayed.
488   UINT flags = MB_OKCANCEL | MB_ICONWARNING;
489   if (is_rtl_locale)
490     flags |= MB_RIGHT | MB_RTLREADING;
491
492   return WrapMessageBoxWithSEH(base::UTF16ToWide(message).c_str(),
493                                base::UTF16ToWide(title).c_str(),
494                                flags,
495                                exit_now);
496 }
497
498 // Crashes the process after generating a dump for the provided exception. Note
499 // that the crash reporter should be initialized before calling this function
500 // for it to do anything.
501 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the
502 // the name or signature of this function you will break SyzyASAN instrumented
503 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so!
504 extern "C" int __declspec(dllexport) CrashForException(
505     EXCEPTION_POINTERS* info) {
506   if (g_breakpad) {
507     g_breakpad->WriteMinidumpForException(info);
508     // Patched stub exists based on conditions (See InitCrashReporter).
509     // As a side note this function also gets called from
510     // WindowProcExceptionFilter.
511     if (g_real_terminate_process_stub == NULL) {
512       ::TerminateProcess(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
513     } else {
514       NtTerminateProcessPtr real_terminate_proc =
515           reinterpret_cast<NtTerminateProcessPtr>(
516               static_cast<char*>(g_real_terminate_process_stub));
517       real_terminate_proc(::GetCurrentProcess(), content::RESULT_CODE_KILLED);
518     }
519   }
520   return EXCEPTION_CONTINUE_SEARCH;
521 }
522
523 static NTSTATUS WINAPI HookNtTerminateProcess(HANDLE ProcessHandle,
524                                               NTSTATUS ExitStatus) {
525   if (g_breakpad &&
526       (ProcessHandle == ::GetCurrentProcess() || ProcessHandle == NULL)) {
527     NT_TIB* tib = reinterpret_cast<NT_TIB*>(NtCurrentTeb());
528     void* address_on_stack = _AddressOfReturnAddress();
529     if (address_on_stack < tib->StackLimit ||
530         address_on_stack > tib->StackBase) {
531       g_surrogate_exception_record.ExceptionAddress = _ReturnAddress();
532       g_surrogate_exception_record.ExceptionCode = DBG_TERMINATE_PROCESS;
533       g_surrogate_exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
534       CrashForException(&g_surrogate_exception_pointers);
535     }
536   }
537
538   NtTerminateProcessPtr real_proc =
539       reinterpret_cast<NtTerminateProcessPtr>(
540           static_cast<char*>(g_real_terminate_process_stub));
541   return real_proc(ProcessHandle, ExitStatus);
542 }
543
544 static void InitTerminateProcessHooks() {
545   NtTerminateProcessPtr terminate_process_func_address =
546       reinterpret_cast<NtTerminateProcessPtr>(::GetProcAddress(
547           ::GetModuleHandle(L"ntdll.dll"), "NtTerminateProcess"));
548   if (terminate_process_func_address == NULL)
549     return;
550
551   DWORD old_protect = 0;
552   if (!::VirtualProtect(terminate_process_func_address, 5,
553                         PAGE_EXECUTE_READWRITE, &old_protect))
554     return;
555
556   g_real_terminate_process_stub = reinterpret_cast<char*>(VirtualAllocEx(
557       ::GetCurrentProcess(), NULL, sidestep::kMaxPreambleStubSize,
558       MEM_COMMIT, PAGE_EXECUTE_READWRITE));
559   if (g_real_terminate_process_stub == NULL)
560     return;
561
562   g_surrogate_exception_pointers.ContextRecord = &g_surrogate_context;
563   g_surrogate_exception_pointers.ExceptionRecord =
564       &g_surrogate_exception_record;
565
566   sidestep::SideStepError patch_result =
567       sidestep::PreamblePatcher::Patch(
568           terminate_process_func_address, HookNtTerminateProcess,
569           g_real_terminate_process_stub, sidestep::kMaxPreambleStubSize);
570   if (patch_result != sidestep::SIDESTEP_SUCCESS) {
571     CHECK(::VirtualFreeEx(::GetCurrentProcess(), g_real_terminate_process_stub,
572                     0, MEM_RELEASE));
573     CHECK(::VirtualProtect(terminate_process_func_address, 5, old_protect,
574                            &old_protect));
575     return;
576   }
577
578   DWORD dummy = 0;
579   CHECK(::VirtualProtect(terminate_process_func_address,
580                          5,
581                          old_protect,
582                          &dummy));
583   CHECK(::VirtualProtect(g_real_terminate_process_stub,
584                          sidestep::kMaxPreambleStubSize,
585                          old_protect,
586                          &old_protect));
587 }
588
589 static void InitPipeNameEnvVar(bool is_per_user_install) {
590   scoped_ptr<base::Environment> env(base::Environment::Create());
591   if (env->HasVar(kPipeNameVar)) {
592     // The Breakpad pipe name is already configured: nothing to do.
593     return;
594   }
595
596   // Check whether configuration management controls crash reporting.
597   bool crash_reporting_enabled = true;
598   bool controlled_by_policy = GetBreakpadClient()->ReportingIsEnforcedByPolicy(
599       &crash_reporting_enabled);
600
601   const CommandLine& command = *CommandLine::ForCurrentProcess();
602   bool use_crash_service =
603       !controlled_by_policy && (command.HasSwitch(switches::kNoErrorDialogs) ||
604                                 GetBreakpadClient()->IsRunningUnattended());
605
606   std::wstring pipe_name;
607   if (use_crash_service) {
608     // Crash reporting is done by crash_service.exe.
609     pipe_name = kChromePipeName;
610   } else {
611     // We want to use the Google Update crash reporting. We need to check if the
612     // user allows it first (in case the administrator didn't already decide
613     // via policy).
614     if (!controlled_by_policy)
615       crash_reporting_enabled = GetBreakpadClient()->GetCollectStatsConsent();
616
617     if (!crash_reporting_enabled) {
618       // Crash reporting is disabled, don't set the environment variable.
619       return;
620     }
621
622     // Build the pipe name. It can be either:
623     // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18"
624     // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>"
625     std::wstring user_sid;
626     if (is_per_user_install) {
627       if (!base::win::GetUserSidString(&user_sid)) {
628         return;
629       }
630     } else {
631       user_sid = kSystemPrincipalSid;
632     }
633
634     pipe_name = kGoogleUpdatePipeName;
635     pipe_name += user_sid;
636   }
637   env->SetVar(kPipeNameVar, base::UTF16ToASCII(pipe_name));
638 }
639
640 void InitDefaultCrashCallback(LPTOP_LEVEL_EXCEPTION_FILTER filter) {
641   previous_filter = SetUnhandledExceptionFilter(filter);
642 }
643
644 void InitCrashReporter(const std::string& process_type_switch) {
645   const CommandLine& command = *CommandLine::ForCurrentProcess();
646   if (command.HasSwitch(switches::kDisableBreakpad))
647     return;
648
649   // Disable the message box for assertions.
650   _CrtSetReportMode(_CRT_ASSERT, 0);
651
652   std::wstring process_type = base::ASCIIToWide(process_type_switch);
653   if (process_type.empty())
654     process_type = L"browser";
655
656   wchar_t exe_path[MAX_PATH];
657   exe_path[0] = 0;
658   GetModuleFileNameW(NULL, exe_path, MAX_PATH);
659
660   bool is_per_user_install =
661       GetBreakpadClient()->GetIsPerUserInstall(base::FilePath(exe_path));
662
663   google_breakpad::CustomClientInfo* custom_info =
664       GetCustomInfo(exe_path, process_type);
665
666   google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL;
667   LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL;
668   // We install the post-dump callback only for the browser and service
669   // processes. It spawns a new browser/service process.
670   if (process_type == L"browser") {
671     callback = &DumpDoneCallback;
672     default_filter = &ChromeExceptionFilter;
673   } else if (process_type == L"service") {
674     callback = &DumpDoneCallback;
675     default_filter = &ServiceExceptionFilter;
676   }
677
678   if (process_type == L"browser") {
679     InitPipeNameEnvVar(is_per_user_install);
680     GetBreakpadClient()->InitBrowserCrashDumpsRegKey();
681   }
682
683   scoped_ptr<base::Environment> env(base::Environment::Create());
684   std::string pipe_name_ascii;
685   if (!env->GetVar(kPipeNameVar, &pipe_name_ascii)) {
686     // Breakpad is not enabled.  Configuration is managed or the user
687     // did not allow Google Update to send crashes.  We need to use
688     // our default crash handler instead, but only for the
689     // browser/service processes.
690     if (default_filter)
691       InitDefaultCrashCallback(default_filter);
692     return;
693   }
694   std::wstring pipe_name = base::ASCIIToWide(pipe_name_ascii);
695
696 #ifdef _WIN64
697   // The protocol for connecting to the out-of-process Breakpad crash
698   // reporter is different for x86-32 and x86-64: the message sizes
699   // are different because the message struct contains a pointer.  As
700   // a result, there are two different named pipes to connect to.  The
701   // 64-bit one is distinguished with an "-x64" suffix.
702   pipe_name += L"-x64";
703 #endif
704
705   // Get the alternate dump directory. We use the temp path.
706   wchar_t temp_dir[MAX_PATH] = {0};
707   ::GetTempPathW(MAX_PATH, temp_dir);
708
709   MINIDUMP_TYPE dump_type = kSmallDumpType;
710   // Capture full memory if explicitly instructed to.
711   if (command.HasSwitch(switches::kFullMemoryCrashReport))
712     dump_type = kFullDumpType;
713   else if (GetBreakpadClient()->GetShouldDumpLargerDumps(is_per_user_install))
714     dump_type = kLargerDumpType;
715
716   g_breakpad = new google_breakpad::ExceptionHandler(temp_dir, &FilterCallback,
717                    callback, NULL,
718                    google_breakpad::ExceptionHandler::HANDLER_ALL,
719                    dump_type, pipe_name.c_str(), custom_info);
720
721   // Now initialize the non crash dump handler.
722   g_dumphandler_no_crash = new google_breakpad::ExceptionHandler(temp_dir,
723       &FilterCallbackWhenNoCrash,
724       &DumpDoneCallbackWhenNoCrash,
725       NULL,
726       // Set the handler to none so this handler would not be added to
727       // |handler_stack_| in |ExceptionHandler| which is a list of exception
728       // handlers.
729       google_breakpad::ExceptionHandler::HANDLER_NONE,
730       dump_type, pipe_name.c_str(), custom_info);
731
732   base::debug::SetDumpWithoutCrashingFunction(&DumpProcessWithoutCrash);
733
734   if (g_breakpad->IsOutOfProcess()) {
735     // Tells breakpad to handle breakpoint and single step exceptions.
736     // This might break JIT debuggers, but at least it will always
737     // generate a crashdump for these exceptions.
738     g_breakpad->set_handle_debug_exceptions(true);
739
740 #ifndef _WIN64
741     if (process_type != L"browser" &&
742         !GetBreakpadClient()->IsRunningUnattended()) {
743       // Initialize the hook TerminateProcess to catch unexpected exits.
744       InitTerminateProcessHooks();
745     }
746 #endif
747   }
748 }
749
750 }  // namespace breakpad