• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 Google LLC
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are
5 // met:
6 //
7 //     * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //     * Redistributions in binary form must reproduce the above
10 // copyright notice, this list of conditions and the following disclaimer
11 // in the documentation and/or other materials provided with the
12 // distribution.
13 //     * Neither the name of Google LLC nor the names of its
14 // contributors may be used to endorse or promote products derived from
15 // this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>  // Must come first
31 #endif
32 
33 #include <windows.h>
34 
35 #include <string>
36 
37 #include "breakpad_googletest_includes.h"
38 #include "client/windows/handler/exception_handler.h"
39 #include "client/windows/unittests/exception_handler_test.h"
40 
41 namespace {
42 
43 const char kFoo[] = "foo";
44 const char kBar[] = "bar";
45 
46 const char kStartOfLine[] = "^";
47 const char kEndOfLine[] = "$";
48 
49 const char kFilterReturnsTrue[] = "filter_returns_true";
50 const char kFilterReturnsFalse[] = "filter_returns_false";
51 
52 const char kCallbackReturnsTrue[] = "callback_returns_true";
53 const char kCallbackReturnsFalse[] = "callback_returns_false";
54 
DoesPathExist(const wchar_t * path_name)55 bool DoesPathExist(const wchar_t* path_name) {
56   DWORD flags = GetFileAttributes(path_name);
57   if (flags == INVALID_FILE_ATTRIBUTES) {
58     return false;
59   }
60   return true;
61 }
62 
63 // A callback function to run before Breakpad performs any substantial
64 // processing of an exception.  A FilterCallback is called before writing
65 // a minidump.  context is the parameter supplied by the user as
66 // callback_context when the handler was created.  exinfo points to the
67 // exception record, if any; assertion points to assertion information,
68 // if any.
69 //
70 // If a FilterCallback returns true, Breakpad will continue processing,
71 // attempting to write a minidump.  If a FilterCallback returns false,
72 // Breakpad will immediately report the exception as unhandled without
73 // writing a minidump, allowing another handler the opportunity to handle it.
74 template <bool filter_return_value>
CrashHandlerFilter(void * context,EXCEPTION_POINTERS * exinfo,MDRawAssertionInfo * assertion)75 bool CrashHandlerFilter(void* context,
76                         EXCEPTION_POINTERS* exinfo,
77                         MDRawAssertionInfo* assertion) {
78   if (filter_return_value) {
79     fprintf(stderr, kFilterReturnsTrue);
80   } else {
81     fprintf(stderr, kFilterReturnsFalse);
82   }
83   fflush(stderr);
84 
85   return filter_return_value;
86 }
87 
88 // A callback function to run after the minidump has been written.
89 // minidump_id is a unique id for the dump, so the minidump
90 // file is <dump_path>\<minidump_id>.dmp.  context is the parameter supplied
91 // by the user as callback_context when the handler was created.  exinfo
92 // points to the exception record, or NULL if no exception occurred.
93 // succeeded indicates whether a minidump file was successfully written.
94 // assertion points to information about an assertion if the handler was
95 // invoked by an assertion.
96 //
97 // If an exception occurred and the callback returns true, Breakpad will treat
98 // the exception as fully-handled, suppressing any other handlers from being
99 // notified of the exception.  If the callback returns false, Breakpad will
100 // treat the exception as unhandled, and allow another handler to handle it.
101 // If there are no other handlers, Breakpad will report the exception to the
102 // system as unhandled, allowing a debugger or native crash dialog the
103 // opportunity to handle the exception.  Most callback implementations
104 // should normally return the value of |succeeded|, or when they wish to
105 // not report an exception of handled, false.  Callbacks will rarely want to
106 // return true directly (unless |succeeded| is true).
107 //
108 // For out-of-process dump generation, dump path and minidump ID will always
109 // be NULL. In case of out-of-process dump generation, the dump path and
110 // minidump id are controlled by the server process and are not communicated
111 // back to the crashing process.
112 template <bool callback_return_value>
MinidumpWrittenCallback(const wchar_t * dump_path,const wchar_t * minidump_id,void * context,EXCEPTION_POINTERS * exinfo,MDRawAssertionInfo * assertion,bool succeeded)113 bool MinidumpWrittenCallback(const wchar_t* dump_path,
114                              const wchar_t* minidump_id,
115                              void* context,
116                              EXCEPTION_POINTERS* exinfo,
117                              MDRawAssertionInfo* assertion,
118                              bool succeeded) {
119   bool rv = false;
120   if (callback_return_value &&
121       succeeded &&
122       DoesPathExist(dump_path)) {
123     rv = true;
124     fprintf(stderr, kCallbackReturnsTrue);
125   } else {
126     fprintf(stderr, kCallbackReturnsFalse);
127   }
128   fflush(stderr);
129 
130   return rv;
131 }
132 
133 
DoCrash(const char * message)134 void DoCrash(const char* message) {
135   if (message) {
136     fprintf(stderr, "%s", message);
137     fflush(stderr);
138   }
139   int* i = NULL;
140   (*i)++;
141 
142   ASSERT_TRUE(false);
143 }
144 
InstallExceptionHandlerAndCrash(bool install_filter,bool filter_return_value,bool install_callback,bool callback_return_value)145 void InstallExceptionHandlerAndCrash(bool install_filter,
146                                      bool filter_return_value,
147                                      bool install_callback,
148                                      bool callback_return_value) {
149   wchar_t temp_path[MAX_PATH] = { '\0' };
150   GetTempPath(MAX_PATH, temp_path);
151 
152   ASSERT_TRUE(DoesPathExist(temp_path));
153   google_breakpad::ExceptionHandler exc(
154       temp_path,
155       install_filter ?
156         (filter_return_value ?
157           &CrashHandlerFilter<true> :
158           &CrashHandlerFilter<false>) :
159         NULL,
160       install_callback ?
161         (callback_return_value ?
162           &MinidumpWrittenCallback<true> :
163           &MinidumpWrittenCallback<false>) :
164         NULL,
165       NULL,  // callback_context
166       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
167 
168   // Disable GTest SEH handler
169   testing::DisableExceptionHandlerInScope disable_exception_handler;
170 
171   DoCrash(NULL);
172 }
173 
TEST(AssertDeathSanity,Simple)174 TEST(AssertDeathSanity, Simple) {
175   ASSERT_DEATH(DoCrash(NULL), "");
176 }
177 
TEST(AssertDeathSanity,Regex)178 TEST(AssertDeathSanity, Regex) {
179   ASSERT_DEATH(DoCrash(kFoo),
180     std::string(kStartOfLine) +
181       std::string(kFoo) +
182       std::string(kEndOfLine));
183 
184   ASSERT_DEATH(DoCrash(kBar),
185     std::string(kStartOfLine) +
186       std::string(kBar) +
187       std::string(kEndOfLine));
188 }
189 
TEST(ExceptionHandlerCallbacks,FilterTrue_No_Callback)190 TEST(ExceptionHandlerCallbacks, FilterTrue_No_Callback) {
191   ASSERT_DEATH(
192     InstallExceptionHandlerAndCrash(true,    // install_filter
193                                     true,    // filter_return_value
194                                     false,   // install_callback
195                                     false),  // callback_return_value
196     std::string(kStartOfLine) +
197       std::string(kFilterReturnsTrue) +
198       std::string(kEndOfLine));
199 }
200 
TEST(ExceptionHandlerCallbacks,FilterTrue_Callback)201 TEST(ExceptionHandlerCallbacks, FilterTrue_Callback) {
202   ASSERT_DEATH(
203     InstallExceptionHandlerAndCrash(true,    // install_filter
204                                     true,    // filter_return_value
205                                     true,    // install_callback
206                                     false),  // callback_return_value
207     std::string(kStartOfLine) +
208       std::string(kFilterReturnsTrue) +
209       std::string(kCallbackReturnsFalse) +
210       std::string(kEndOfLine));
211 }
212 
TEST(ExceptionHandlerCallbacks,FilterFalse_No_Callback)213 TEST(ExceptionHandlerCallbacks, FilterFalse_No_Callback) {
214   ASSERT_DEATH(
215     InstallExceptionHandlerAndCrash(true,    // install_filter
216                                     false,   // filter_return_value
217                                     false,   // install_callback
218                                     false),  // callback_return_value
219     std::string(kStartOfLine) +
220       std::string(kFilterReturnsFalse) +
221       std::string(kEndOfLine));
222 }
223 
224 // Callback shouldn't be executed when filter returns false
TEST(ExceptionHandlerCallbacks,FilterFalse_Callback)225 TEST(ExceptionHandlerCallbacks, FilterFalse_Callback) {
226   ASSERT_DEATH(
227     InstallExceptionHandlerAndCrash(true,    // install_filter
228                                     false,   // filter_return_value
229                                     true,    // install_callback
230                                     false),  // callback_return_value
231     std::string(kStartOfLine) +
232       std::string(kFilterReturnsFalse) +
233       std::string(kEndOfLine));
234 }
235 
TEST(ExceptionHandlerCallbacks,No_Filter_No_Callback)236 TEST(ExceptionHandlerCallbacks, No_Filter_No_Callback) {
237   ASSERT_DEATH(
238     InstallExceptionHandlerAndCrash(false,   // install_filter
239                                     true,    // filter_return_value
240                                     false,   // install_callback
241                                     false),  // callback_return_value
242     std::string(kStartOfLine) +
243       std::string(kEndOfLine));
244 }
245 
TEST(ExceptionHandlerCallbacks,No_Filter_Callback)246 TEST(ExceptionHandlerCallbacks, No_Filter_Callback) {
247   ASSERT_DEATH(
248     InstallExceptionHandlerAndCrash(false,   // install_filter
249                                     true,    // filter_return_value
250                                     true,    // install_callback
251                                     false),  // callback_return_value
252     std::string(kStartOfLine) +
253       std::string(kCallbackReturnsFalse) +
254       std::string(kEndOfLine));
255 }
256 
257 
TEST(ExceptionHandlerNesting,Skip_From_Inner_Filter)258 TEST(ExceptionHandlerNesting, Skip_From_Inner_Filter) {
259   wchar_t temp_path[MAX_PATH] = { '\0' };
260   GetTempPath(MAX_PATH, temp_path);
261 
262   ASSERT_TRUE(DoesPathExist(temp_path));
263   google_breakpad::ExceptionHandler exc(
264       temp_path,
265       &CrashHandlerFilter<true>,
266       &MinidumpWrittenCallback<false>,
267       NULL,  // callback_context
268       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
269 
270   ASSERT_DEATH(
271     InstallExceptionHandlerAndCrash(true,   // install_filter
272                                     false,  // filter_return_value
273                                     true,   // install_callback
274                                     true),  // callback_return_value
275     std::string(kStartOfLine) +
276       std::string(kFilterReturnsFalse) +    // inner filter
277       std::string(kFilterReturnsTrue) +     // outer filter
278       std::string(kCallbackReturnsFalse) +  // outer callback
279       std::string(kEndOfLine));
280 }
281 
TEST(ExceptionHandlerNesting,Skip_From_Inner_Callback)282 TEST(ExceptionHandlerNesting, Skip_From_Inner_Callback) {
283   wchar_t temp_path[MAX_PATH] = { '\0' };
284   GetTempPath(MAX_PATH, temp_path);
285 
286   ASSERT_TRUE(DoesPathExist(temp_path));
287   google_breakpad::ExceptionHandler exc(
288       temp_path,
289       &CrashHandlerFilter<true>,
290       &MinidumpWrittenCallback<false>,
291       NULL,  // callback_context
292       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
293 
294   ASSERT_DEATH(
295     InstallExceptionHandlerAndCrash(true,    // install_filter
296                                     true,    // filter_return_value
297                                     true,    // install_callback
298                                     false),  // callback_return_value
299     std::string(kStartOfLine) +
300       std::string(kFilterReturnsTrue) +      // inner filter
301       std::string(kCallbackReturnsFalse) +   // inner callback
302       std::string(kFilterReturnsTrue) +      // outer filter
303       std::string(kCallbackReturnsFalse) +   // outer callback
304       std::string(kEndOfLine));
305 }
306 
TEST(ExceptionHandlerNesting,Handled_By_Inner_Handler)307 TEST(ExceptionHandlerNesting, Handled_By_Inner_Handler) {
308   wchar_t temp_path[MAX_PATH] = { '\0' };
309   GetTempPath(MAX_PATH, temp_path);
310 
311   ASSERT_TRUE(DoesPathExist(temp_path));
312   google_breakpad::ExceptionHandler exc(
313       temp_path,
314       &CrashHandlerFilter<true>,
315       &MinidumpWrittenCallback<true>,
316       NULL,  // callback_context
317       google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
318 
319   ASSERT_DEATH(
320     InstallExceptionHandlerAndCrash(true,   // install_filter
321                                     true,   // filter_return_value
322                                     true,   // install_callback
323                                     true),  // callback_return_value
324     std::string(kStartOfLine) +
325       std::string(kFilterReturnsTrue) +    // inner filter
326       std::string(kCallbackReturnsTrue) +  // inner callback
327       std::string(kEndOfLine));
328 }
329 
330 }  // namespace
331