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