| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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 "chrome_elf/crash/crash_helper.h" | |
| 6 | |
| 7 #include <assert.h> | |
| 8 #include <windows.h> | |
| 9 | |
| 10 #include <algorithm> | |
| 11 #include <string> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "chrome/app/chrome_crash_reporter_client_win.h" | |
| 15 #include "chrome_elf/hook_util/hook_util.h" | |
| 16 #include "components/crash/content/app/crashpad.h" | |
| 17 #include "components/crash/core/common/crash_keys.h" | |
| 18 #include "third_party/crashpad/crashpad/client/crashpad_client.h" | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // Crash handling from elf is only enabled for the chrome.exe process. | |
| 23 // Use this global to safely handle the rare case where elf may not be in that | |
| 24 // process (e.g. tests). | |
| 25 bool g_crash_helper_enabled = false; | |
| 26 | |
| 27 // Gets the exe name from the full path of the exe. | |
| 28 bool GetExeName(std::wstring* exe_name) { | |
| 29 std::wstring file_name; | |
| 30 file_name.resize(MAX_PATH); | |
| 31 if (!::GetModuleFileNameW(nullptr, &file_name[0], MAX_PATH)) { | |
| 32 assert(false); | |
| 33 return false; | |
| 34 } | |
| 35 size_t last_slash_pos = file_name.find_last_of(L'\\'); | |
| 36 if (last_slash_pos != std::wstring::npos) { | |
| 37 *exe_name = file_name.substr(last_slash_pos + 1, | |
| 38 file_name.length() - last_slash_pos); | |
| 39 } | |
| 40 std::transform(exe_name->begin(), exe_name->end(), exe_name->begin(), | |
| 41 ::tolower); | |
| 42 return true; | |
| 43 } | |
| 44 | |
| 45 // Global pointer to a vector of crash reports. | |
| 46 // This structure will be initialized in InitializeCrashReportingForProcess() | |
| 47 // and cleaned up in DllDetachCrashReportingCleanup(). | |
| 48 std::vector<crash_reporter::Report>* g_crash_reports = nullptr; | |
| 49 | |
| 50 // chrome_elf loads early in the process and initializes Crashpad. That in turn | |
| 51 // uses the SetUnhandledExceptionFilter API to set a top level exception | |
| 52 // handler for the process. When the process eventually initializes, CRT sets | |
| 53 // an exception handler which calls TerminateProcess which effectively bypasses | |
| 54 // us. Ideally we want to be at the top of the unhandled exception filter | |
| 55 // chain. However we don't have a good way of intercepting the | |
| 56 // SetUnhandledExceptionFilter API in the sandbox. EAT patching kernel32 or | |
| 57 // kernelbase should ideally work. However the kernel32 kernelbase dlls are | |
| 58 // prebound which causes EAT patching to not work. Sidestep works. However it | |
| 59 // is only supported for 32 bit. For now we use IAT patching for the | |
| 60 // executable. | |
| 61 // TODO(ananta). | |
| 62 // Check if it is possible to fix EAT patching or use sidestep patching for | |
| 63 // 32 bit and 64 bit for this purpose. | |
| 64 elf_hook::IATHook* g_set_unhandled_exception_filter = nullptr; | |
| 65 | |
| 66 // Hook function, which ignores the request to set an unhandled-exception | |
| 67 // filter. | |
| 68 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI | |
| 69 SetUnhandledExceptionFilterPatch(LPTOP_LEVEL_EXCEPTION_FILTER filter) { | |
| 70 // Don't set the exception filter. Please see above for comments. | |
| 71 return nullptr; | |
| 72 } | |
| 73 | |
| 74 } // namespace | |
| 75 | |
| 76 //------------------------------------------------------------------------------ | |
| 77 // Public chrome_elf crash APIs | |
| 78 //------------------------------------------------------------------------------ | |
| 79 | |
| 80 namespace elf_crash { | |
| 81 | |
| 82 // NOTE: This function will be called from DllMain during DLL_PROCESS_ATTACH | |
| 83 // (while we have the loader lock), so do not misbehave. | |
| 84 bool InitializeCrashReporting() { | |
| 85 // We want to initialize crash reporting only in chrome.exe | |
| 86 std::wstring proc_name; | |
| 87 if (!GetExeName(&proc_name) || | |
| 88 0 != ::wcscmp(proc_name.data(), L"chrome.exe")) { | |
| 89 #ifdef _DEBUG | |
| 90 assert(false); | |
| 91 #endif // _DEBUG | |
| 92 return false; | |
| 93 } | |
| 94 | |
| 95 #ifdef _DEBUG | |
| 96 assert(g_crash_reports == nullptr); | |
| 97 assert(g_set_unhandled_exception_filter == nullptr); | |
| 98 #endif // _DEBUG | |
| 99 | |
| 100 // No global objects with destructors, so using global pointers. | |
| 101 // DllMain on detach will clean these up. | |
| 102 g_crash_reports = new std::vector<crash_reporter::Report>; | |
| 103 g_set_unhandled_exception_filter = new elf_hook::IATHook(); | |
| 104 | |
| 105 ChromeCrashReporterClient::InitializeCrashReportingForProcess(); | |
| 106 | |
| 107 g_crash_helper_enabled = true; | |
| 108 return true; | |
| 109 } | |
| 110 | |
| 111 // NOTE: This function will be called from DllMain during DLL_PROCESS_DETACH | |
| 112 // (while we have the loader lock), so do not misbehave. | |
| 113 void ShutdownCrashReporting() { | |
| 114 if (g_crash_reports != nullptr) { | |
| 115 g_crash_reports->clear(); | |
| 116 delete g_crash_reports; | |
| 117 } | |
| 118 if (g_set_unhandled_exception_filter != nullptr) { | |
| 119 delete g_set_unhandled_exception_filter; | |
| 120 } | |
| 121 } | |
| 122 | |
| 123 // Please refer to the comment on g_set_unhandled_exception_filter for more | |
| 124 // information about why we intercept the SetUnhandledExceptionFilter API. | |
| 125 void DisableSetUnhandledExceptionFilter() { | |
| 126 if (!g_crash_helper_enabled) | |
| 127 return; | |
| 128 if (g_set_unhandled_exception_filter->Hook( | |
| 129 ::GetModuleHandle(nullptr), "kernel32.dll", | |
| 130 "SetUnhandledExceptionFilter", | |
| 131 SetUnhandledExceptionFilterPatch) != NO_ERROR) { | |
| 132 #ifdef _DEBUG | |
| 133 assert(false); | |
| 134 #endif //_DEBUG | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 int GenerateCrashDump(EXCEPTION_POINTERS* exception_pointers) { | |
| 139 if (g_crash_helper_enabled) | |
| 140 crashpad::CrashpadClient::DumpWithoutCrash( | |
| 141 *(exception_pointers->ContextRecord)); | |
| 142 return EXCEPTION_CONTINUE_SEARCH; | |
| 143 } | |
| 144 | |
| 145 } // namespace elf_crash | |
| 146 | |
| 147 //------------------------------------------------------------------------------ | |
| 148 // Exported crash APIs for the rest of the process. | |
| 149 //------------------------------------------------------------------------------ | |
| 150 | |
| 151 // This helper is invoked by code in chrome.dll to retrieve the crash reports. | |
| 152 // See CrashUploadListCrashpad. Note that we do not pass a std::vector here, | |
| 153 // because we do not want to allocate/free in different modules. The returned | |
| 154 // pointer is read-only. | |
| 155 // | |
| 156 // NOTE: Since the returned pointer references read-only memory that will be | |
| 157 // cleaned up when this DLL unloads, be careful not to reference the memory | |
| 158 // beyond that point (e.g. during tests). | |
| 159 extern "C" __declspec(dllexport) void GetCrashReportsImpl( | |
| 160 const crash_reporter::Report** reports, | |
| 161 size_t* report_count) { | |
| 162 if (!g_crash_helper_enabled) | |
| 163 return; | |
| 164 crash_reporter::GetReports(g_crash_reports); | |
| 165 *reports = g_crash_reports->data(); | |
| 166 *report_count = g_crash_reports->size(); | |
| 167 } | |
| 168 | |
| 169 // This helper is invoked by debugging code in chrome to register the client | |
| 170 // id. | |
| 171 extern "C" __declspec(dllexport) void SetMetricsClientId( | |
| 172 const char* client_id) { | |
| 173 if (!g_crash_helper_enabled) | |
| 174 return; | |
| 175 if (client_id) | |
| 176 crash_keys::SetMetricsClientIdFromGUID(client_id); | |
| 177 } | |
| OLD | NEW |