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