OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/crashpad.h" |
| 6 |
| 7 #include "base/environment.h" |
| 8 #include "base/lazy_instance.h" |
| 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/numerics/safe_conversions.h" |
| 11 #include "base/path_service.h" |
| 12 #include "base/strings/string16.h" |
| 13 #include "base/strings/string_number_conversions.h" |
| 14 #include "base/strings/utf_string_conversions.h" |
| 15 #include "build/build_config.h" |
| 16 #include "components/crash/content/app/crash_reporter_client.h" |
| 17 #include "third_party/crashpad/crashpad/client/crashpad_client.h" |
| 18 #include "third_party/crashpad/crashpad/client/crashpad_info.h" |
| 19 |
| 20 namespace crash_reporter { |
| 21 namespace internal { |
| 22 |
| 23 namespace { |
| 24 |
| 25 base::LazyInstance<crashpad::CrashpadClient>::Leaky g_crashpad_client = |
| 26 LAZY_INSTANCE_INITIALIZER; |
| 27 |
| 28 } // namespace |
| 29 |
| 30 base::FilePath PlatformCrashpadInitialization(bool initial_client, |
| 31 bool browser_process) { |
| 32 base::FilePath database_path; // Only valid in the browser process. |
| 33 bool result; |
| 34 |
| 35 const char kPipeNameVar[] = "CHROME_CRASHPAD_PIPE_NAME"; |
| 36 scoped_ptr<base::Environment> env(base::Environment::Create()); |
| 37 |
| 38 DCHECK_EQ(initial_client, browser_process); |
| 39 |
| 40 if (initial_client) { |
| 41 CrashReporterClient* crash_reporter_client = GetCrashReporterClient(); |
| 42 crash_reporter_client->GetCrashDumpLocation(&database_path); |
| 43 |
| 44 base::FilePath exe_file; |
| 45 CHECK(PathService::Get(base::FILE_EXE, &exe_file)); |
| 46 base::string16 product_name, version, special_build, channel_name; |
| 47 crash_reporter_client->GetProductNameAndVersion( |
| 48 exe_file, &product_name, &version, &special_build, &channel_name); |
| 49 std::map<std::string, std::string> process_annotations; |
| 50 process_annotations["prod"] = base::UTF16ToUTF8(product_name); |
| 51 process_annotations["ver"] = base::UTF16ToUTF8(version); |
| 52 process_annotations["channel"] = base::UTF16ToUTF8(channel_name); |
| 53 if (!special_build.empty()) |
| 54 process_annotations["special"] = base::UTF16ToUTF8(special_build); |
| 55 #if defined(ARCH_CPU_X86) |
| 56 process_annotations["plat"] = std::string("Win32"); |
| 57 #elif defined(ARCH_CPU_X86_64) |
| 58 process_annotations["plat"] = std::string("Win64"); |
| 59 #endif |
| 60 #if defined(GOOGLE_CHROME_BUILD) |
| 61 std::string url = "https://clients2.google.com/cr/report"; |
| 62 #else |
| 63 std::string url; |
| 64 #endif |
| 65 |
| 66 std::vector<std::string> arguments; |
| 67 |
| 68 // In test binaries, use crashpad_handler directly. Otherwise, we launch |
| 69 // chrome.exe with --type=crashpad-handler. |
| 70 if (exe_file.BaseName().value() != FILE_PATH_LITERAL("chrome.exe")) { |
| 71 base::FilePath exe_dir; |
| 72 CHECK(PathService::Get(base::DIR_EXE, &exe_dir)); |
| 73 exe_file = exe_dir.Append(FILE_PATH_LITERAL("crashpad_handler.exe")); |
| 74 } else { |
| 75 arguments.push_back("--type=crashpad-handler"); |
| 76 } |
| 77 |
| 78 result = g_crashpad_client.Get().StartHandler( |
| 79 exe_file, database_path, url, process_annotations, arguments, false); |
| 80 |
| 81 // If we're the browser, push the pipe name into the environment so child |
| 82 // processes can connect to it. If we inherited another crashpad_handler's |
| 83 // pipe name, we'll overwrite it here. |
| 84 env->SetVar(kPipeNameVar, |
| 85 base::UTF16ToUTF8(g_crashpad_client.Get().GetHandlerIPCPipe())); |
| 86 } else { |
| 87 std::string pipe_name_utf8; |
| 88 result = env->GetVar(kPipeNameVar, &pipe_name_utf8); |
| 89 if (result) { |
| 90 result = g_crashpad_client.Get().SetHandlerIPCPipe( |
| 91 base::UTF8ToUTF16(pipe_name_utf8)); |
| 92 } |
| 93 } |
| 94 |
| 95 if (result) { |
| 96 result = g_crashpad_client.Get().UseHandler(); |
| 97 } |
| 98 |
| 99 return database_path; |
| 100 } |
| 101 |
| 102 extern "C" { |
| 103 |
| 104 // Crashes the process after generating a dump for the provided exception. Note |
| 105 // that the crash reporter should be initialized before calling this function |
| 106 // for it to do anything. |
| 107 // NOTE: This function is used by SyzyASAN to invoke a crash. If you change the |
| 108 // the name or signature of this function you will break SyzyASAN instrumented |
| 109 // releases of Chrome. Please contact syzygy-team@chromium.org before doing so! |
| 110 int __declspec(dllexport) CrashForException( |
| 111 EXCEPTION_POINTERS* info) { |
| 112 g_crashpad_client.Get().DumpAndCrash(info); |
| 113 return EXCEPTION_CONTINUE_SEARCH; |
| 114 } |
| 115 |
| 116 #if defined(ARCH_CPU_X86_64) |
| 117 |
| 118 static int CrashForExceptionInNonABICompliantCodeRange( |
| 119 PEXCEPTION_RECORD ExceptionRecord, |
| 120 ULONG64 EstablisherFrame, |
| 121 PCONTEXT ContextRecord, |
| 122 PDISPATCHER_CONTEXT DispatcherContext) { |
| 123 EXCEPTION_POINTERS info = { ExceptionRecord, ContextRecord }; |
| 124 return CrashForException(&info); |
| 125 } |
| 126 |
| 127 // See https://msdn.microsoft.com/en-us/library/ddssxxy8.aspx |
| 128 typedef struct _UNWIND_INFO { |
| 129 unsigned char Version : 3; |
| 130 unsigned char Flags : 5; |
| 131 unsigned char SizeOfProlog; |
| 132 unsigned char CountOfCodes; |
| 133 unsigned char FrameRegister : 4; |
| 134 unsigned char FrameOffset : 4; |
| 135 ULONG ExceptionHandler; |
| 136 } UNWIND_INFO, *PUNWIND_INFO; |
| 137 |
| 138 struct ExceptionHandlerRecord { |
| 139 RUNTIME_FUNCTION runtime_function; |
| 140 UNWIND_INFO unwind_info; |
| 141 unsigned char thunk[12]; |
| 142 }; |
| 143 |
| 144 // These are GetProcAddress()d from V8 binding code. |
| 145 void __declspec(dllexport) __cdecl RegisterNonABICompliantCodeRange( |
| 146 void* start, |
| 147 size_t size_in_bytes) { |
| 148 ExceptionHandlerRecord* record = |
| 149 reinterpret_cast<ExceptionHandlerRecord*>(start); |
| 150 |
| 151 // We assume that the first page of the code range is executable and |
| 152 // committed and reserved for breakpad. What could possibly go wrong? |
| 153 |
| 154 // All addresses are 32bit relative offsets to start. |
| 155 record->runtime_function.BeginAddress = 0; |
| 156 record->runtime_function.EndAddress = |
| 157 base::checked_cast<DWORD>(size_in_bytes); |
| 158 record->runtime_function.UnwindData = |
| 159 offsetof(ExceptionHandlerRecord, unwind_info); |
| 160 |
| 161 // Create unwind info that only specifies an exception handler. |
| 162 record->unwind_info.Version = 1; |
| 163 record->unwind_info.Flags = UNW_FLAG_EHANDLER; |
| 164 record->unwind_info.SizeOfProlog = 0; |
| 165 record->unwind_info.CountOfCodes = 0; |
| 166 record->unwind_info.FrameRegister = 0; |
| 167 record->unwind_info.FrameOffset = 0; |
| 168 record->unwind_info.ExceptionHandler = |
| 169 offsetof(ExceptionHandlerRecord, thunk); |
| 170 |
| 171 // Hardcoded thunk. |
| 172 // mov imm64, rax |
| 173 record->thunk[0] = 0x48; |
| 174 record->thunk[1] = 0xb8; |
| 175 void* handler = &CrashForExceptionInNonABICompliantCodeRange; |
| 176 memcpy(&record->thunk[2], &handler, 8); |
| 177 |
| 178 // jmp rax |
| 179 record->thunk[10] = 0xff; |
| 180 record->thunk[11] = 0xe0; |
| 181 |
| 182 // Protect reserved page against modifications. |
| 183 DWORD old_protect; |
| 184 CHECK(VirtualProtect( |
| 185 start, sizeof(ExceptionHandlerRecord), PAGE_EXECUTE_READ, &old_protect)); |
| 186 CHECK(RtlAddFunctionTable( |
| 187 &record->runtime_function, 1, reinterpret_cast<DWORD64>(start))); |
| 188 } |
| 189 |
| 190 void __declspec(dllexport) __cdecl UnregisterNonABICompliantCodeRange( |
| 191 void* start) { |
| 192 ExceptionHandlerRecord* record = |
| 193 reinterpret_cast<ExceptionHandlerRecord*>(start); |
| 194 |
| 195 CHECK(RtlDeleteFunctionTable(&record->runtime_function)); |
| 196 } |
| 197 #endif // ARCH_CPU_X86_64 |
| 198 |
| 199 } // extern "C" |
| 200 |
| 201 } // namespace internal |
| 202 } // namespace crash_reporter |
OLD | NEW |