1 // Copyright 2012, 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 <windows.h>
31
32 #include <string>
33
34 #include "breakpad_googletest_includes.h"
35 #include "client/windows/handler/exception_handler.h"
36 #include "client/windows/unittests/exception_handler_test.h"
37
38 namespace {
39
40 const char kFoo[] = "foo";
41 const char kBar[] = "bar";
42
43 const char kStartOfLine[] = "^";
44 const char kEndOfLine[] = "$";
45
46 const char kFilterReturnsTrue[] = "filter_returns_true";
47 const char kFilterReturnsFalse[] = "filter_returns_false";
48
49 const char kCallbackReturnsTrue[] = "callback_returns_true";
50 const char kCallbackReturnsFalse[] = "callback_returns_false";
51
DoesPathExist(const wchar_t * path_name)52 bool DoesPathExist(const wchar_t *path_name) {
53 DWORD flags = GetFileAttributes(path_name);
54 if (flags == INVALID_FILE_ATTRIBUTES) {
55 return false;
56 }
57 return true;
58 }
59
60 // A callback function to run before Breakpad performs any substantial
61 // processing of an exception. A FilterCallback is called before writing
62 // a minidump. context is the parameter supplied by the user as
63 // callback_context when the handler was created. exinfo points to the
64 // exception record, if any; assertion points to assertion information,
65 // if any.
66 //
67 // If a FilterCallback returns true, Breakpad will continue processing,
68 // attempting to write a minidump. If a FilterCallback returns false,
69 // Breakpad will immediately report the exception as unhandled without
70 // writing a minidump, allowing another handler the opportunity to handle it.
71 template <bool filter_return_value>
CrashHandlerFilter(void * context,EXCEPTION_POINTERS * exinfo,MDRawAssertionInfo * assertion)72 bool CrashHandlerFilter(void* context,
73 EXCEPTION_POINTERS* exinfo,
74 MDRawAssertionInfo* assertion) {
75 if (filter_return_value) {
76 fprintf(stderr, kFilterReturnsTrue);
77 } else {
78 fprintf(stderr, kFilterReturnsFalse);
79 }
80 fflush(stderr);
81
82 return filter_return_value;
83 }
84
85 // A callback function to run after the minidump has been written.
86 // minidump_id is a unique id for the dump, so the minidump
87 // file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
88 // by the user as callback_context when the handler was created. exinfo
89 // points to the exception record, or NULL if no exception occurred.
90 // succeeded indicates whether a minidump file was successfully written.
91 // assertion points to information about an assertion if the handler was
92 // invoked by an assertion.
93 //
94 // If an exception occurred and the callback returns true, Breakpad will treat
95 // the exception as fully-handled, suppressing any other handlers from being
96 // notified of the exception. If the callback returns false, Breakpad will
97 // treat the exception as unhandled, and allow another handler to handle it.
98 // If there are no other handlers, Breakpad will report the exception to the
99 // system as unhandled, allowing a debugger or native crash dialog the
100 // opportunity to handle the exception. Most callback implementations
101 // should normally return the value of |succeeded|, or when they wish to
102 // not report an exception of handled, false. Callbacks will rarely want to
103 // return true directly (unless |succeeded| is true).
104 //
105 // For out-of-process dump generation, dump path and minidump ID will always
106 // be NULL. In case of out-of-process dump generation, the dump path and
107 // minidump id are controlled by the server process and are not communicated
108 // back to the crashing process.
109 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)110 bool MinidumpWrittenCallback(const wchar_t* dump_path,
111 const wchar_t* minidump_id,
112 void* context,
113 EXCEPTION_POINTERS* exinfo,
114 MDRawAssertionInfo* assertion,
115 bool succeeded) {
116 bool rv = false;
117 if (callback_return_value &&
118 succeeded &&
119 DoesPathExist(dump_path)) {
120 rv = true;
121 fprintf(stderr, kCallbackReturnsTrue);
122 } else {
123 fprintf(stderr, kCallbackReturnsFalse);
124 }
125 fflush(stderr);
126
127 return rv;
128 }
129
130
DoCrash(const char * message)131 void DoCrash(const char *message) {
132 if (message) {
133 fprintf(stderr, "%s", message);
134 fflush(stderr);
135 }
136 int *i = NULL;
137 (*i)++;
138
139 ASSERT_TRUE(false);
140 }
141
InstallExceptionHandlerAndCrash(bool install_filter,bool filter_return_value,bool install_callback,bool callback_return_value)142 void InstallExceptionHandlerAndCrash(bool install_filter,
143 bool filter_return_value,
144 bool install_callback,
145 bool callback_return_value) {
146 wchar_t temp_path[MAX_PATH] = { '\0' };
147 GetTempPath(MAX_PATH, temp_path);
148
149 ASSERT_TRUE(DoesPathExist(temp_path));
150 google_breakpad::ExceptionHandler exc(
151 temp_path,
152 install_filter ?
153 (filter_return_value ?
154 &CrashHandlerFilter<true> :
155 &CrashHandlerFilter<false>) :
156 NULL,
157 install_callback ?
158 (callback_return_value ?
159 &MinidumpWrittenCallback<true> :
160 &MinidumpWrittenCallback<false>) :
161 NULL,
162 NULL, // callback_context
163 google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
164
165 // Disable GTest SEH handler
166 testing::DisableExceptionHandlerInScope disable_exception_handler;
167
168 DoCrash(NULL);
169 }
170
TEST(AssertDeathSanity,Simple)171 TEST(AssertDeathSanity, Simple) {
172 ASSERT_DEATH(DoCrash(NULL), "");
173 }
174
TEST(AssertDeathSanity,Regex)175 TEST(AssertDeathSanity, Regex) {
176 ASSERT_DEATH(DoCrash(kFoo),
177 std::string(kStartOfLine) +
178 std::string(kFoo) +
179 std::string(kEndOfLine));
180
181 ASSERT_DEATH(DoCrash(kBar),
182 std::string(kStartOfLine) +
183 std::string(kBar) +
184 std::string(kEndOfLine));
185 }
186
TEST(ExceptionHandlerCallbacks,FilterTrue_No_Callback)187 TEST(ExceptionHandlerCallbacks, FilterTrue_No_Callback) {
188 ASSERT_DEATH(
189 InstallExceptionHandlerAndCrash(true, // install_filter
190 true, // filter_return_value
191 false, // install_callback
192 false), // callback_return_value
193 std::string(kStartOfLine) +
194 std::string(kFilterReturnsTrue) +
195 std::string(kEndOfLine));
196 }
197
TEST(ExceptionHandlerCallbacks,FilterTrue_Callback)198 TEST(ExceptionHandlerCallbacks, FilterTrue_Callback) {
199 ASSERT_DEATH(
200 InstallExceptionHandlerAndCrash(true, // install_filter
201 true, // filter_return_value
202 true, // install_callback
203 false), // callback_return_value
204 std::string(kStartOfLine) +
205 std::string(kFilterReturnsTrue) +
206 std::string(kCallbackReturnsFalse) +
207 std::string(kEndOfLine));
208 }
209
TEST(ExceptionHandlerCallbacks,FilterFalse_No_Callback)210 TEST(ExceptionHandlerCallbacks, FilterFalse_No_Callback) {
211 ASSERT_DEATH(
212 InstallExceptionHandlerAndCrash(true, // install_filter
213 false, // filter_return_value
214 false, // install_callback
215 false), // callback_return_value
216 std::string(kStartOfLine) +
217 std::string(kFilterReturnsFalse) +
218 std::string(kEndOfLine));
219 }
220
221 // Callback shouldn't be executed when filter returns false
TEST(ExceptionHandlerCallbacks,FilterFalse_Callback)222 TEST(ExceptionHandlerCallbacks, FilterFalse_Callback) {
223 ASSERT_DEATH(
224 InstallExceptionHandlerAndCrash(true, // install_filter
225 false, // filter_return_value
226 true, // install_callback
227 false), // callback_return_value
228 std::string(kStartOfLine) +
229 std::string(kFilterReturnsFalse) +
230 std::string(kEndOfLine));
231 }
232
TEST(ExceptionHandlerCallbacks,No_Filter_No_Callback)233 TEST(ExceptionHandlerCallbacks, No_Filter_No_Callback) {
234 ASSERT_DEATH(
235 InstallExceptionHandlerAndCrash(false, // install_filter
236 true, // filter_return_value
237 false, // install_callback
238 false), // callback_return_value
239 std::string(kStartOfLine) +
240 std::string(kEndOfLine));
241 }
242
TEST(ExceptionHandlerCallbacks,No_Filter_Callback)243 TEST(ExceptionHandlerCallbacks, No_Filter_Callback) {
244 ASSERT_DEATH(
245 InstallExceptionHandlerAndCrash(false, // install_filter
246 true, // filter_return_value
247 true, // install_callback
248 false), // callback_return_value
249 std::string(kStartOfLine) +
250 std::string(kCallbackReturnsFalse) +
251 std::string(kEndOfLine));
252 }
253
254
TEST(ExceptionHandlerNesting,Skip_From_Inner_Filter)255 TEST(ExceptionHandlerNesting, Skip_From_Inner_Filter) {
256 wchar_t temp_path[MAX_PATH] = { '\0' };
257 GetTempPath(MAX_PATH, temp_path);
258
259 ASSERT_TRUE(DoesPathExist(temp_path));
260 google_breakpad::ExceptionHandler exc(
261 temp_path,
262 &CrashHandlerFilter<true>,
263 &MinidumpWrittenCallback<false>,
264 NULL, // callback_context
265 google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
266
267 ASSERT_DEATH(
268 InstallExceptionHandlerAndCrash(true, // install_filter
269 false, // filter_return_value
270 true, // install_callback
271 true), // callback_return_value
272 std::string(kStartOfLine) +
273 std::string(kFilterReturnsFalse) + // inner filter
274 std::string(kFilterReturnsTrue) + // outer filter
275 std::string(kCallbackReturnsFalse) + // outer callback
276 std::string(kEndOfLine));
277 }
278
TEST(ExceptionHandlerNesting,Skip_From_Inner_Callback)279 TEST(ExceptionHandlerNesting, Skip_From_Inner_Callback) {
280 wchar_t temp_path[MAX_PATH] = { '\0' };
281 GetTempPath(MAX_PATH, temp_path);
282
283 ASSERT_TRUE(DoesPathExist(temp_path));
284 google_breakpad::ExceptionHandler exc(
285 temp_path,
286 &CrashHandlerFilter<true>,
287 &MinidumpWrittenCallback<false>,
288 NULL, // callback_context
289 google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
290
291 ASSERT_DEATH(
292 InstallExceptionHandlerAndCrash(true, // install_filter
293 true, // filter_return_value
294 true, // install_callback
295 false), // callback_return_value
296 std::string(kStartOfLine) +
297 std::string(kFilterReturnsTrue) + // inner filter
298 std::string(kCallbackReturnsFalse) + // inner callback
299 std::string(kFilterReturnsTrue) + // outer filter
300 std::string(kCallbackReturnsFalse) + // outer callback
301 std::string(kEndOfLine));
302 }
303
TEST(ExceptionHandlerNesting,Handled_By_Inner_Handler)304 TEST(ExceptionHandlerNesting, Handled_By_Inner_Handler) {
305 wchar_t temp_path[MAX_PATH] = { '\0' };
306 GetTempPath(MAX_PATH, temp_path);
307
308 ASSERT_TRUE(DoesPathExist(temp_path));
309 google_breakpad::ExceptionHandler exc(
310 temp_path,
311 &CrashHandlerFilter<true>,
312 &MinidumpWrittenCallback<true>,
313 NULL, // callback_context
314 google_breakpad::ExceptionHandler::HANDLER_EXCEPTION);
315
316 ASSERT_DEATH(
317 InstallExceptionHandlerAndCrash(true, // install_filter
318 true, // filter_return_value
319 true, // install_callback
320 true), // callback_return_value
321 std::string(kStartOfLine) +
322 std::string(kFilterReturnsTrue) + // inner filter
323 std::string(kCallbackReturnsTrue) + // inner callback
324 std::string(kEndOfLine));
325 }
326
327 } // namespace
328