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
38namespace {
39
40const char kFoo[] = "foo";
41const char kBar[] = "bar";
42
43const char kStartOfLine[] = "^";
44const char kEndOfLine[] = "$";
45
46const char kFilterReturnsTrue[] = "filter_returns_true";
47const char kFilterReturnsFalse[] = "filter_returns_false";
48
49const char kCallbackReturnsTrue[] = "callback_returns_true";
50const char kCallbackReturnsFalse[] = "callback_returns_false";
51
52bool 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.
71template <bool filter_return_value>
72bool 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.
109template <bool callback_return_value>
110bool 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
131void 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
142void 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
171TEST(AssertDeathSanity, Simple) {
172  ASSERT_DEATH(DoCrash(NULL), "");
173}
174
175TEST(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
187TEST(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
198TEST(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
210TEST(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
222TEST(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
233TEST(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
243TEST(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
255TEST(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
279TEST(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
304TEST(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