1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #include "base/crash_report_handler.h"
31
32 #if defined(OS_WIN) && defined(GOOGLE_JAPANESE_INPUT_BUILD)
33
34 #include <Windows.h>
35 #include <ShellAPI.h> // for CommandLineToArgvW
36
37 #include <cstdlib>
38 #include <string>
39
40 #include "base/const.h"
41 #include "base/file_util.h"
42 #include "base/logging.h"
43 #include "base/system_util.h"
44 #include "base/util.h"
45 #include "base/version.h"
46 #include "third_party/breakpad/src/client/windows/handler/exception_handler.h"
47
48 namespace {
49
50 // The prefix of the pipe name which GoogleCrashHandler.exe opens for clients
51 // to register them.
52 const wchar_t kGoogleCrashHandlerPipePrefix[] =
53 L"\\\\.\\pipe\\GoogleCrashServices\\";
54
55 // This is the well known SID for the system principal.
56 const wchar_t kSystemPrincipalSid[] =L"S-1-5-18";
57
58 // The postfix of the pipe name which GoogleCrashHandler.exe opens for clients
59 // to register them.
60 // Covert: On x86 environment, both _M_X64 and _M_IX86 are defined. So we need
61 // to check _M_X64 first.
62 #if defined(_M_X64)
63 // x64 crash handler expects the postfix "-64".
64 // See b/5166654 or http://crbug.com/89730 for the background info.
65 const wchar_t kGoogleCrashHandlerPipePostfix[] =L"-x64";
66 #elif defined(_M_IX86)
67 // No postfix for the x86 crash handler.
68 const wchar_t kGoogleCrashHandlerPipePostfix[] =L"";
69 #else
70 #error "unsupported platform"
71 #endif
72
73 // The product name registered in the crash server.
74 const wchar_t kProductNameInCrash[] = L"Google_Japanese_IME";
75
76 // The reference count for ExceptionHandler.
77 int g_reference_count = 0;
78
79 // The CRITICAL_SECTION struct used for creating or deleting ExceptionHandler in
80 // a mutually exclusive manner.
81 CRITICAL_SECTION *g_critical_section = NULL;
82
83 google_breakpad::ExceptionHandler *g_handler = NULL;
84
85 // Returns the name of the build mode.
GetBuildMode()86 std::wstring GetBuildMode() {
87 #if defined(NO_LOGGING)
88 return L"rel";
89 #elif defined(DEBUG)
90 return L"dbg";
91 #else
92 return L"opt";
93 #endif
94 }
95
96 // Reduces the size of the string |str| to a max of 64 chars (Extra 1 char is
97 // trimmed for NULL-terminator so effective characters are 63 characters).
98 // Required because breakpad's CustomInfoEntry raises an invalid_parameter error
99 // if the string we want to set is longer than 64 characters, including
100 // NULL-terminator.
TrimToBreakpadMax(const std::wstring & str)101 std::wstring TrimToBreakpadMax(const std::wstring &str) {
102 std::wstring shorter(str);
103 return shorter.substr(0,
104 google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
105 }
106
107 // Returns the custom info structure based on the dll in parameter and the
108 // process type.
GetCustomInfo()109 google_breakpad::CustomClientInfo* GetCustomInfo() {
110 // Common entries.
111 google_breakpad::CustomInfoEntry ver_entry(L"ver",
112 mozc::Version::GetMozcVersionW().c_str());
113 google_breakpad::CustomInfoEntry prod_entry(L"prod", kProductNameInCrash);
114 google_breakpad::CustomInfoEntry buildmode_entry(L"Build Mode",
115 GetBuildMode().c_str());
116
117 // Get the first two command line switches if they exist.
118 google_breakpad::CustomInfoEntry switch1(L"switch-1", L"");
119 google_breakpad::CustomInfoEntry switch2(L"switch-2", L"");
120 {
121 int num_args = 0;
122 wchar_t** args = ::CommandLineToArgvW(::GetCommandLineW(), &num_args);
123 if (args != NULL) {
124 if (num_args > 1) {
125 switch1.set_value(TrimToBreakpadMax(args[1]).c_str());
126 }
127 if (num_args > 2) {
128 switch2.set_value(TrimToBreakpadMax(args[2]).c_str());
129 }
130 // Caller is responsible to free the returned value of CommandLineToArgv.
131 ::LocalFree(args);
132 }
133 }
134
135 static google_breakpad::CustomInfoEntry entries[] =
136 {ver_entry, prod_entry, buildmode_entry, switch1, switch2};
137 static google_breakpad::CustomClientInfo custom_info =
138 {entries, arraysize(entries)};
139
140 return &custom_info;
141 }
142
143 // Returns the pipe name of the GoogleCrashHandler.exe or
144 // GoogleCrashHandler64.exe running as a system user.
GetCrashHandlerPipeName()145 std::wstring GetCrashHandlerPipeName() {
146 std::wstring pipe_name = kGoogleCrashHandlerPipePrefix;
147 pipe_name.append(kSystemPrincipalSid);
148 pipe_name.append(kGoogleCrashHandlerPipePostfix);
149 return pipe_name;
150 }
151
152 class ScopedCriticalSection {
153 public:
ScopedCriticalSection(CRITICAL_SECTION * critical_section)154 explicit ScopedCriticalSection(CRITICAL_SECTION *critical_section)
155 : critical_section_(critical_section) {
156 if (critical_section_ != NULL) {
157 EnterCriticalSection(critical_section_);
158 }
159 }
~ScopedCriticalSection()160 ~ScopedCriticalSection() {
161 if (critical_section_ != NULL) {
162 LeaveCriticalSection(critical_section_);
163 }
164 }
165
166 private:
167 CRITICAL_SECTION *critical_section_;
168 };
169
170 // get the handle to the module containing the given executing address
GetModuleHandleFromAddress(void * address)171 HMODULE GetModuleHandleFromAddress(void *address) {
172 // address may be NULL
173 MEMORY_BASIC_INFORMATION mbi;
174 SIZE_T result = VirtualQuery(address, &mbi, sizeof(mbi));
175 if (0 == result) {
176 return NULL;
177 }
178 return static_cast<HMODULE>(mbi.AllocationBase);
179 }
180
181 // get the handle to the currently executing module
GetCurrentModuleHandle()182 HMODULE GetCurrentModuleHandle() {
183 return GetModuleHandleFromAddress(GetCurrentModuleHandle);
184 }
185
186 // Check to see if an address is in the current module.
IsAddressInCurrentModule(void * address)187 bool IsAddressInCurrentModule(void *address) {
188 // address may be NULL
189 return GetCurrentModuleHandle() == GetModuleHandleFromAddress(address);
190 }
191
IsCurrentModuleInStack(PCONTEXT context)192 bool IsCurrentModuleInStack(PCONTEXT context) {
193 STACKFRAME64 stack;
194 ZeroMemory(&stack, sizeof(stack));
195 // This macro is defined for x86 processors.
196 // See http://msdn.microsoft.com/ja-jp/library/b0084kay(VS.80).aspx for details.
197 #if defined(_M_IX86)
198 stack.AddrPC.Offset = context->Eip;
199 stack.AddrPC.Mode = AddrModeFlat;
200 stack.AddrStack.Offset = context->Esp;
201 stack.AddrStack.Mode = AddrModeFlat;
202 stack.AddrFrame.Offset = context->Ebp;
203 stack.AddrFrame.Mode = AddrModeFlat;
204 // This macro is defined for x64 processors.
205 #elif defined(_M_X64)
206 stack.AddrPC.Offset = context->Rip;
207 stack.AddrPC.Mode = AddrModeFlat;
208 stack.AddrStack.Offset = context->Rsp;
209 stack.AddrStack.Mode = AddrModeFlat;
210 stack.AddrFrame.Offset = context->Rbp;
211 stack.AddrFrame.Mode = AddrModeFlat;
212 #else
213 #error "unsupported platform"
214 #endif
215
216 while (StackWalk64(IMAGE_FILE_MACHINE_I386,
217 GetCurrentProcess(),
218 GetCurrentThread(),
219 &stack,
220 context,
221 0,
222 SymFunctionTableAccess64,
223 SymGetModuleBase64,
224 0)) {
225 if (IsAddressInCurrentModule(
226 reinterpret_cast<void*>(stack.AddrPC.Offset))) {
227 return true;
228 }
229 }
230 return false;
231 }
232
FilterHandler(void * context,EXCEPTION_POINTERS * exinfo,MDRawAssertionInfo * assertion)233 bool FilterHandler(void *context, EXCEPTION_POINTERS *exinfo,
234 MDRawAssertionInfo *assertion) {
235 if (exinfo == NULL) {
236 // We do not catch CRT error in release build.
237 #ifdef NO_LOGGING
238 return false;
239 #else
240 return true;
241 #endif
242 }
243
244 // Make sure it's our module which cause the crash.
245 if (IsAddressInCurrentModule(exinfo->ExceptionRecord->ExceptionAddress)) {
246 return true;
247 }
248
249 if (IsCurrentModuleInStack(exinfo->ContextRecord)) {
250 return true;
251 }
252
253 return false;
254 }
255
256 } // namespace
257
258
259 namespace mozc {
260
Initialize(bool check_address)261 bool CrashReportHandler::Initialize(bool check_address) {
262 ScopedCriticalSection critical_section(g_critical_section);
263 DCHECK_GE(g_reference_count, 0);
264 ++g_reference_count;
265 if (g_reference_count == 1 && g_handler == NULL) {
266 const string acrashdump_directory = SystemUtil::GetCrashReportDirectory();
267 // create a crash dump directory if not exist.
268 if (!FileUtil::FileExists(acrashdump_directory)) {
269 FileUtil::CreateDirectory(acrashdump_directory);
270 }
271
272 std::wstring crashdump_directory;
273 Util::UTF8ToWide(acrashdump_directory, &crashdump_directory);
274
275 google_breakpad::ExceptionHandler::FilterCallback filter_callback =
276 check_address ? FilterHandler : NULL;
277 const auto kCrashDumpType = static_cast<MINIDUMP_TYPE>(
278 MiniDumpWithUnloadedModules | MiniDumpWithProcessThreadData);
279 g_handler = new google_breakpad::ExceptionHandler(
280 crashdump_directory.c_str(),
281 filter_callback,
282 NULL, // MinidumpCallback
283 NULL, // callback_context
284 google_breakpad::ExceptionHandler::HANDLER_ALL,
285 kCrashDumpType,
286 GetCrashHandlerPipeName().c_str(),
287 GetCustomInfo());
288
289 #ifdef DEBUG
290 g_handler->set_handle_debug_exceptions(true);
291 #endif // DEBUG
292 return true;
293 }
294 return false;
295 }
296
IsInitialized()297 bool CrashReportHandler::IsInitialized() {
298 ScopedCriticalSection critical_section(g_critical_section);
299 return g_reference_count > 0;
300 }
301
Uninitialize()302 bool CrashReportHandler::Uninitialize() {
303 ScopedCriticalSection critical_section(g_critical_section);
304 --g_reference_count;
305 DCHECK_GE(g_reference_count, 0);
306 if (g_reference_count == 0 && g_handler != NULL) {
307 delete g_handler;
308 g_handler = NULL;
309 return true;
310 }
311 return false;
312 }
313
SetCriticalSection(CRITICAL_SECTION * critical_section)314 void CrashReportHandler::SetCriticalSection(
315 CRITICAL_SECTION *critical_section) {
316 g_critical_section = critical_section;
317 }
318
319 } // namespace mozc
320
321 #else // OS_WIN && GOOGLE_JAPANESE_INPUT_BUILD
322
323 namespace mozc {
324
325 // Null implementation for platforms where we do not want to enable breakpad.
326
Initialize(bool check_address)327 bool CrashReportHandler::Initialize(bool check_address) {
328 return false;
329 }
330
IsInitialized()331 bool CrashReportHandler::IsInitialized() {
332 return false;
333 }
334
Uninitialize()335 bool CrashReportHandler::Uninitialize() {
336 return false;
337 }
338
339 #ifdef OS_WIN
SetCriticalSection(CRITICAL_SECTION * critical_section)340 void CrashReportHandler::SetCriticalSection(
341 CRITICAL_SECTION *critical_section) {
342 }
343 #endif // OS_WIN
344
345 } // namespace mozc
346
347 #endif // OS_WIN && GOOGLE_JAPANESE_INPUT_BUILD
348