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 |