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