blob: adea044fab6ba2951dd6bd96be69b24d9fb76a44 [file] [log] [blame]
hansl@google.com8cf0a522010-05-12 17:51:21 +00001// Copyright 2009, 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#include <dbghelp.h>
32#include <strsafe.h>
33#include <objbase.h>
34#include <shellapi.h>
35
ted.mielczarek0df05552011-01-25 19:19:19 +000036#include <string>
37
hansl@google.com8cf0a522010-05-12 17:51:21 +000038#include "../../../breakpad_googletest_includes.h"
ted.mielczarek0df05552011-01-25 19:19:19 +000039#include "../../../../common/windows/string_utils-inl.h"
hansl@google.com8cf0a522010-05-12 17:51:21 +000040#include "../crash_generation/crash_generation_server.h"
41#include "../handler/exception_handler.h"
ted.mielczarek0df05552011-01-25 19:19:19 +000042#include "../../../../google_breakpad/processor/minidump.h"
hansl@google.com8cf0a522010-05-12 17:51:21 +000043
44namespace {
ted.mielczarek0df05552011-01-25 19:19:19 +000045
46using std::wstring;
47using namespace google_breakpad;
48
hansl@google.com8cf0a522010-05-12 17:51:21 +000049const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
50const char kSuccessIndicator[] = "success";
51const char kFailureIndicator[] = "failure";
52
53// Utility function to test for a path's existence.
54BOOL DoesPathExist(const TCHAR *path_name);
55
56class ExceptionHandlerDeathTest : public ::testing::Test {
57 protected:
58 // Member variable for each test that they can use
59 // for temporary storage.
60 TCHAR temp_path_[MAX_PATH];
61 // Actually constructs a temp path name.
62 virtual void SetUp();
63 // A helper method that tests can use to crash.
hansl@google.combcf885c2010-05-13 21:00:43 +000064 void DoCrashAccessViolation();
65 void DoCrashPureVirtualCall();
hansl@google.com8cf0a522010-05-12 17:51:21 +000066};
67
68void ExceptionHandlerDeathTest::SetUp() {
69 const ::testing::TestInfo* const test_info =
70 ::testing::UnitTest::GetInstance()->current_test_info();
71 TCHAR temp_path[MAX_PATH] = { '\0' };
72 TCHAR test_name_wide[MAX_PATH] = { '\0' };
73 // We want the temporary directory to be what the OS returns
74 // to us, + the test case name.
75 GetTempPath(MAX_PATH, temp_path);
ted.mielczarek0df05552011-01-25 19:19:19 +000076 // The test case name is exposed as a c-style string,
77 // convert it to a wchar_t string.
hansl@google.com8cf0a522010-05-12 17:51:21 +000078 int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
79 strlen(test_info->name()),
80 test_name_wide,
81 MAX_PATH);
82 if (!dwRet) {
83 assert(false);
84 }
85 StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
86 CreateDirectory(temp_path_, NULL);
87}
88
89BOOL DoesPathExist(const TCHAR *path_name) {
90 DWORD flags = GetFileAttributes(path_name);
91 if (flags == INVALID_FILE_ATTRIBUTES) {
92 return FALSE;
93 }
94 return TRUE;
95}
96
97bool MinidumpWrittenCallback(const wchar_t* dump_path,
98 const wchar_t* minidump_id,
99 void* context,
100 EXCEPTION_POINTERS* exinfo,
101 MDRawAssertionInfo* assertion,
102 bool succeeded) {
103 if (succeeded && DoesPathExist(dump_path)) {
104 fprintf(stderr, kSuccessIndicator);
105 } else {
106 fprintf(stderr, kFailureIndicator);
107 }
108 // If we don't flush, the output doesn't get sent before
109 // this process dies.
110 fflush(stderr);
111 return succeeded;
112}
113
114TEST_F(ExceptionHandlerDeathTest, InProcTest) {
115 // For the in-proc test, we just need to instantiate an exception
116 // handler in in-proc mode, and crash. Since the entire test is
117 // reexecuted in the child process, we don't have to worry about
118 // the semantics of the exception handler being inherited/not
119 // inherited across CreateProcess().
120 ASSERT_TRUE(DoesPathExist(temp_path_));
121 google_breakpad::ExceptionHandler *exc =
122 new google_breakpad::ExceptionHandler(
123 temp_path_, NULL, &MinidumpWrittenCallback, NULL,
124 google_breakpad::ExceptionHandler::HANDLER_ALL);
125 int *i = NULL;
126 ASSERT_DEATH((*i)++, kSuccessIndicator);
127 delete exc;
128}
129
130static bool gDumpCallbackCalled = false;
131
132void clientDumpCallback(void *dump_context,
133 const google_breakpad::ClientInfo *client_info,
134 const std::wstring *dump_path) {
135 gDumpCallbackCalled = true;
136}
137
hansl@google.combcf885c2010-05-13 21:00:43 +0000138void ExceptionHandlerDeathTest::DoCrashAccessViolation() {
hansl@google.com8cf0a522010-05-12 17:51:21 +0000139 google_breakpad::ExceptionHandler *exc =
140 new google_breakpad::ExceptionHandler(
141 temp_path_, NULL, NULL, NULL,
142 google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName,
143 NULL);
144 // Although this is executing in the child process of the death test,
145 // if it's not true we'll still get an error rather than the crash
146 // being expected.
147 ASSERT_TRUE(exc->IsOutOfProcess());
148 int *i = NULL;
149 printf("%d\n", (*i)++);
150}
151
152TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) {
153 // We can take advantage of a detail of google test here to save some
154 // complexity in testing: when you do a death test, it actually forks.
155 // So we can make the main test harness the crash generation server,
156 // and call ASSERT_DEATH on a NULL dereference, it to expecting test
157 // the out of process scenario, since it's happening in a different
158 // process! This is different from the above because, above, we pass
159 // a NULL pipe name, and we also don't start a crash generation server.
160
161 ASSERT_TRUE(DoesPathExist(temp_path_));
162 std::wstring dump_path(temp_path_);
163 google_breakpad::CrashGenerationServer server(
164 kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true,
165 &dump_path);
166
167 // This HAS to be EXPECT_, because when this test case is executed in the
168 // child process, the server registration will fail due to the named pipe
169 // being the same.
170 EXPECT_TRUE(server.Start());
171 EXPECT_FALSE(gDumpCallbackCalled);
hansl@google.combcf885c2010-05-13 21:00:43 +0000172 ASSERT_DEATH(this->DoCrashAccessViolation(), "");
hansl@google.com8cf0a522010-05-12 17:51:21 +0000173 EXPECT_TRUE(gDumpCallbackCalled);
174}
175
176TEST_F(ExceptionHandlerDeathTest, InvalidParameterTest) {
177 using google_breakpad::ExceptionHandler;
178
179 ASSERT_TRUE(DoesPathExist(temp_path_));
180 ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
181 ExceptionHandler::HANDLER_INVALID_PARAMETER);
182
183 // Disable the message box for assertions
184 _CrtSetReportMode(_CRT_ASSERT, 0);
185
186 // Call with a bad argument. The invalid parameter will be swallowed
187 // and a dump will be generated, the process will exit(0).
188 ASSERT_EXIT(printf(NULL), ::testing::ExitedWithCode(0), "");
189}
hansl@google.combcf885c2010-05-13 21:00:43 +0000190
191
192struct PureVirtualCallBase {
193 PureVirtualCallBase() {
194 // We have to reinterpret so the linker doesn't get confused because the
195 // method isn't defined.
196 reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
197 }
198 virtual ~PureVirtualCallBase() {}
199 virtual void PureFunction() const = 0;
200};
201struct PureVirtualCall : public PureVirtualCallBase {
202 PureVirtualCall() { PureFunction(); }
203 virtual void PureFunction() const {}
204};
205
206void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() {
207 PureVirtualCall instance;
208}
209
210TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) {
211 using google_breakpad::ExceptionHandler;
212
213 ASSERT_TRUE(DoesPathExist(temp_path_));
214 ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
215 ExceptionHandler::HANDLER_PURECALL);
216
217 // Disable the message box for assertions
218 _CrtSetReportMode(_CRT_ASSERT, 0);
219
220 // Calls a pure virtual function.
221 EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
222}
ted.mielczarek0df05552011-01-25 19:19:19 +0000223
224wstring find_minidump_in_directory(const wstring &directory) {
225 wstring search_path = directory + L"\\*";
226 WIN32_FIND_DATA find_data;
227 HANDLE find_handle = FindFirstFileW(search_path.c_str(), &find_data);
228 if (find_handle == INVALID_HANDLE_VALUE)
229 return wstring();
230
231 wstring filename;
232 do {
233 const wchar_t extension[] = L".dmp";
234 const int extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
235 const int filename_length = wcslen(find_data.cFileName);
236 if (filename_length > extension_length &&
237 wcsncmp(extension,
238 find_data.cFileName + filename_length - extension_length,
239 extension_length) == 0) {
240 filename = directory + L"\\" + find_data.cFileName;
241 break;
242 }
243 } while(FindNextFile(find_handle, &find_data));
244 FindClose(find_handle);
245 return filename;
hansl@google.com8cf0a522010-05-12 17:51:21 +0000246}
ted.mielczarek0df05552011-01-25 19:19:19 +0000247
248TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
249 ASSERT_TRUE(DoesPathExist(temp_path_));
250 google_breakpad::ExceptionHandler *exc =
251 new google_breakpad::ExceptionHandler(
252 temp_path_, NULL, NULL, NULL,
253 google_breakpad::ExceptionHandler::HANDLER_ALL);
254
255 // Get some executable memory.
256 const u_int32_t kMemorySize = 256; // bytes
257 const int kOffset = kMemorySize / 2;
258 // This crashes with SIGILL on x86/x86-64/arm.
259 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
260 char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
261 kMemorySize,
262 MEM_COMMIT | MEM_RESERVE,
263 PAGE_EXECUTE_READWRITE));
264 ASSERT_TRUE(memory);
265
266 // Write some instructions that will crash. Put them
267 // in the middle of the block of memory, because the
268 // minidump should contain 128 bytes on either side of the
269 // instruction pointer.
270 memcpy(memory + kOffset, instructions, sizeof(instructions));
271
272 // Now execute the instructions, which should crash.
273 typedef void (*void_function)(void);
274 void_function memory_function =
275 reinterpret_cast<void_function>(memory + kOffset);
276 ASSERT_DEATH(memory_function(), "");
277
278 // free the memory.
279 VirtualFree(memory, 0, MEM_RELEASE);
280
281 // Verify that the resulting minidump contains the memory around the IP
282 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
283 ASSERT_FALSE(minidump_filename_wide.empty());
284 string minidump_filename;
285 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
286 &minidump_filename));
287
288 // Read the minidump. Locate the exception record and the
289 // memory list, and then ensure that there is a memory region
290 // in the memory list that covers the instruction pointer from
291 // the exception record.
292 {
293 Minidump minidump(minidump_filename);
294 ASSERT_TRUE(minidump.Read());
295
296 MinidumpException* exception = minidump.GetException();
297 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
298 ASSERT_TRUE(exception);
299 ASSERT_TRUE(memory_list);
300 ASSERT_LT((unsigned)0, memory_list->region_count());
301
302 MinidumpContext* context = exception->GetContext();
303 ASSERT_TRUE(context);
304
305 u_int64_t instruction_pointer;
306 switch (context->GetContextCPU()) {
307 case MD_CONTEXT_X86:
308 instruction_pointer = context->GetContextX86()->eip;
309 break;
310 case MD_CONTEXT_AMD64:
311 instruction_pointer = context->GetContextAMD64()->rip;
312 break;
313 default:
314 FAIL() << "Unknown context CPU: " << context->GetContextCPU();
315 break;
316 }
317
318 MinidumpMemoryRegion* region =
319 memory_list->GetMemoryRegionForAddress(instruction_pointer);
320 ASSERT_TRUE(region);
321
322 EXPECT_EQ(kMemorySize, region->GetSize());
323 const u_int8_t* bytes = region->GetMemory();
324 ASSERT_TRUE(bytes);
325
326 u_int8_t prefix_bytes[kOffset];
327 u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
328 memset(prefix_bytes, 0, sizeof(prefix_bytes));
329 memset(suffix_bytes, 0, sizeof(suffix_bytes));
330 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
331 EXPECT_TRUE(memcmp(bytes + kOffset, instructions,
332 sizeof(instructions)) == 0);
333 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
334 suffix_bytes, sizeof(suffix_bytes)) == 0);
335 }
336
337 DeleteFileW(minidump_filename_wide.c_str());
338}
339
340TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) {
341 ASSERT_TRUE(DoesPathExist(temp_path_));
342 google_breakpad::ExceptionHandler *exc =
343 new google_breakpad::ExceptionHandler(
344 temp_path_, NULL, NULL, NULL,
345 google_breakpad::ExceptionHandler::HANDLER_ALL);
346
347 SYSTEM_INFO sSysInfo; // Useful information about the system
348 GetSystemInfo(&sSysInfo); // Initialize the structure.
349
350 const u_int32_t kMemorySize = 256; // bytes
351 const DWORD kPageSize = sSysInfo.dwPageSize;
352 const int kOffset = 0;
353 // This crashes with SIGILL on x86/x86-64/arm.
354 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
355 // Get some executable memory. Specifically, reserve two pages,
356 // but only commit the second.
357 char* all_memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
358 kPageSize * 2,
359 MEM_RESERVE,
360 PAGE_NOACCESS));
361 ASSERT_TRUE(all_memory);
362 char* memory = all_memory + kPageSize;
363 ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
364 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
365
366 // Write some instructions that will crash. Put them
367 // in the middle of the block of memory, because the
368 // minidump should contain 128 bytes on either side of the
369 // instruction pointer.
370 memcpy(memory + kOffset, instructions, sizeof(instructions));
371
372 // Now execute the instructions, which should crash.
373 typedef void (*void_function)(void);
374 void_function memory_function =
375 reinterpret_cast<void_function>(memory + kOffset);
376 ASSERT_DEATH(memory_function(), "");
377
378 // free the memory.
379 VirtualFree(memory, 0, MEM_RELEASE);
380
381 // Verify that the resulting minidump contains the memory around the IP
382 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
383 ASSERT_FALSE(minidump_filename_wide.empty());
384 string minidump_filename;
385 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
386 &minidump_filename));
387
388 // Read the minidump. Locate the exception record and the
389 // memory list, and then ensure that there is a memory region
390 // in the memory list that covers the instruction pointer from
391 // the exception record.
392 {
393 Minidump minidump(minidump_filename);
394 ASSERT_TRUE(minidump.Read());
395
396 MinidumpException* exception = minidump.GetException();
397 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
398 ASSERT_TRUE(exception);
399 ASSERT_TRUE(memory_list);
400 ASSERT_LT((unsigned)0, memory_list->region_count());
401
402 MinidumpContext* context = exception->GetContext();
403 ASSERT_TRUE(context);
404
405 u_int64_t instruction_pointer;
406 switch (context->GetContextCPU()) {
407 case MD_CONTEXT_X86:
408 instruction_pointer = context->GetContextX86()->eip;
409 break;
410 case MD_CONTEXT_AMD64:
411 instruction_pointer = context->GetContextAMD64()->rip;
412 break;
413 default:
414 FAIL() << "Unknown context CPU: " << context->GetContextCPU();
415 break;
416 }
417
418 MinidumpMemoryRegion* region =
419 memory_list->GetMemoryRegionForAddress(instruction_pointer);
420 ASSERT_TRUE(region);
421
422 EXPECT_EQ(kMemorySize / 2, region->GetSize());
423 const u_int8_t* bytes = region->GetMemory();
424 ASSERT_TRUE(bytes);
425
426 u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
427 memset(suffix_bytes, 0, sizeof(suffix_bytes));
428 EXPECT_TRUE(memcmp(bytes + kOffset,
429 instructions, sizeof(instructions)) == 0);
430 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
431 suffix_bytes, sizeof(suffix_bytes)) == 0);
432 }
433
434 DeleteFileW(minidump_filename_wide.c_str());
435}
436
437TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) {
438 ASSERT_TRUE(DoesPathExist(temp_path_));
439 google_breakpad::ExceptionHandler *exc =
440 new google_breakpad::ExceptionHandler(
441 temp_path_, NULL, NULL, NULL,
442 google_breakpad::ExceptionHandler::HANDLER_ALL);
443
444 SYSTEM_INFO sSysInfo; // Useful information about the system
445 GetSystemInfo(&sSysInfo); // Initialize the structure.
446
447 const DWORD kPageSize = sSysInfo.dwPageSize;
448 // This crashes with SIGILL on x86/x86-64/arm.
449 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
450 const int kOffset = kPageSize - sizeof(instructions);
451 // Get some executable memory. Specifically, reserve two pages,
452 // but only commit the first.
453 char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
454 kPageSize * 2,
455 MEM_RESERVE,
456 PAGE_NOACCESS));
457 ASSERT_TRUE(memory);
458 ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
459 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
460
461 // Write some instructions that will crash.
462 memcpy(memory + kOffset, instructions, sizeof(instructions));
463
464 // Now execute the instructions, which should crash.
465 typedef void (*void_function)(void);
466 void_function memory_function =
467 reinterpret_cast<void_function>(memory + kOffset);
468 ASSERT_DEATH(memory_function(), "");
469
470 // free the memory.
471 VirtualFree(memory, 0, MEM_RELEASE);
472
473 // Verify that the resulting minidump contains the memory around the IP
474 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
475 ASSERT_FALSE(minidump_filename_wide.empty());
476 string minidump_filename;
477 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
478 &minidump_filename));
479
480 // Read the minidump. Locate the exception record and the
481 // memory list, and then ensure that there is a memory region
482 // in the memory list that covers the instruction pointer from
483 // the exception record.
484 {
485 Minidump minidump(minidump_filename);
486 ASSERT_TRUE(minidump.Read());
487
488 MinidumpException* exception = minidump.GetException();
489 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
490 ASSERT_TRUE(exception);
491 ASSERT_TRUE(memory_list);
492 ASSERT_LT((unsigned)0, memory_list->region_count());
493
494 MinidumpContext* context = exception->GetContext();
495 ASSERT_TRUE(context);
496
497 u_int64_t instruction_pointer;
498 switch (context->GetContextCPU()) {
499 case MD_CONTEXT_X86:
500 instruction_pointer = context->GetContextX86()->eip;
501 break;
502 case MD_CONTEXT_AMD64:
503 instruction_pointer = context->GetContextAMD64()->rip;
504 break;
505 default:
506 FAIL() << "Unknown context CPU: " << context->GetContextCPU();
507 break;
508 }
509
510 MinidumpMemoryRegion* region =
511 memory_list->GetMemoryRegionForAddress(instruction_pointer);
512 ASSERT_TRUE(region);
513
514 const size_t kPrefixSize = 128; // bytes
515 EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
516 const u_int8_t* bytes = region->GetMemory();
517 ASSERT_TRUE(bytes);
518
519 u_int8_t prefix_bytes[kPrefixSize];
520 memset(prefix_bytes, 0, sizeof(prefix_bytes));
521 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
522 EXPECT_TRUE(memcmp(bytes + kPrefixSize,
523 instructions, sizeof(instructions)) == 0);
524 }
525
526 DeleteFileW(minidump_filename_wide.c_str());
527}
528
529} // namespace