blob: bac96def91453dc2e38b67619869ec20e89f01af [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,
ted.mielczarek27e54b42011-11-11 19:05:51 +0000134 const google_breakpad::CrashGenerationServer::ClientDumpRequestType request_type,
hansl@google.com8cf0a522010-05-12 17:51:21 +0000135 const std::wstring *dump_path) {
136 gDumpCallbackCalled = true;
137}
138
hansl@google.combcf885c2010-05-13 21:00:43 +0000139void ExceptionHandlerDeathTest::DoCrashAccessViolation() {
hansl@google.com8cf0a522010-05-12 17:51:21 +0000140 google_breakpad::ExceptionHandler *exc =
141 new google_breakpad::ExceptionHandler(
142 temp_path_, NULL, NULL, NULL,
143 google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName,
144 NULL);
145 // Although this is executing in the child process of the death test,
146 // if it's not true we'll still get an error rather than the crash
147 // being expected.
148 ASSERT_TRUE(exc->IsOutOfProcess());
149 int *i = NULL;
150 printf("%d\n", (*i)++);
151}
152
153TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) {
154 // We can take advantage of a detail of google test here to save some
155 // complexity in testing: when you do a death test, it actually forks.
156 // So we can make the main test harness the crash generation server,
157 // and call ASSERT_DEATH on a NULL dereference, it to expecting test
158 // the out of process scenario, since it's happening in a different
159 // process! This is different from the above because, above, we pass
160 // a NULL pipe name, and we also don't start a crash generation server.
161
162 ASSERT_TRUE(DoesPathExist(temp_path_));
163 std::wstring dump_path(temp_path_);
164 google_breakpad::CrashGenerationServer server(
165 kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true,
166 &dump_path);
167
168 // This HAS to be EXPECT_, because when this test case is executed in the
169 // child process, the server registration will fail due to the named pipe
170 // being the same.
171 EXPECT_TRUE(server.Start());
172 EXPECT_FALSE(gDumpCallbackCalled);
hansl@google.combcf885c2010-05-13 21:00:43 +0000173 ASSERT_DEATH(this->DoCrashAccessViolation(), "");
hansl@google.com8cf0a522010-05-12 17:51:21 +0000174 EXPECT_TRUE(gDumpCallbackCalled);
175}
176
177TEST_F(ExceptionHandlerDeathTest, InvalidParameterTest) {
178 using google_breakpad::ExceptionHandler;
179
180 ASSERT_TRUE(DoesPathExist(temp_path_));
181 ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
182 ExceptionHandler::HANDLER_INVALID_PARAMETER);
183
184 // Disable the message box for assertions
185 _CrtSetReportMode(_CRT_ASSERT, 0);
186
187 // Call with a bad argument. The invalid parameter will be swallowed
188 // and a dump will be generated, the process will exit(0).
189 ASSERT_EXIT(printf(NULL), ::testing::ExitedWithCode(0), "");
190}
hansl@google.combcf885c2010-05-13 21:00:43 +0000191
192
193struct PureVirtualCallBase {
194 PureVirtualCallBase() {
195 // We have to reinterpret so the linker doesn't get confused because the
196 // method isn't defined.
197 reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
198 }
199 virtual ~PureVirtualCallBase() {}
200 virtual void PureFunction() const = 0;
201};
202struct PureVirtualCall : public PureVirtualCallBase {
203 PureVirtualCall() { PureFunction(); }
204 virtual void PureFunction() const {}
205};
206
207void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() {
208 PureVirtualCall instance;
209}
210
211TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) {
212 using google_breakpad::ExceptionHandler;
213
214 ASSERT_TRUE(DoesPathExist(temp_path_));
215 ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
216 ExceptionHandler::HANDLER_PURECALL);
217
218 // Disable the message box for assertions
219 _CrtSetReportMode(_CRT_ASSERT, 0);
220
221 // Calls a pure virtual function.
222 EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
223}
ted.mielczarek0df05552011-01-25 19:19:19 +0000224
225wstring find_minidump_in_directory(const wstring &directory) {
226 wstring search_path = directory + L"\\*";
227 WIN32_FIND_DATA find_data;
228 HANDLE find_handle = FindFirstFileW(search_path.c_str(), &find_data);
229 if (find_handle == INVALID_HANDLE_VALUE)
230 return wstring();
231
232 wstring filename;
233 do {
234 const wchar_t extension[] = L".dmp";
235 const int extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
236 const int filename_length = wcslen(find_data.cFileName);
237 if (filename_length > extension_length &&
238 wcsncmp(extension,
239 find_data.cFileName + filename_length - extension_length,
240 extension_length) == 0) {
241 filename = directory + L"\\" + find_data.cFileName;
242 break;
243 }
244 } while(FindNextFile(find_handle, &find_data));
245 FindClose(find_handle);
246 return filename;
hansl@google.com8cf0a522010-05-12 17:51:21 +0000247}
ted.mielczarek0df05552011-01-25 19:19:19 +0000248
249TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) {
250 ASSERT_TRUE(DoesPathExist(temp_path_));
251 google_breakpad::ExceptionHandler *exc =
252 new google_breakpad::ExceptionHandler(
253 temp_path_, NULL, NULL, NULL,
254 google_breakpad::ExceptionHandler::HANDLER_ALL);
255
256 // Get some executable memory.
257 const u_int32_t kMemorySize = 256; // bytes
258 const int kOffset = kMemorySize / 2;
259 // This crashes with SIGILL on x86/x86-64/arm.
260 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
261 char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
262 kMemorySize,
263 MEM_COMMIT | MEM_RESERVE,
264 PAGE_EXECUTE_READWRITE));
265 ASSERT_TRUE(memory);
266
267 // Write some instructions that will crash. Put them
268 // in the middle of the block of memory, because the
269 // minidump should contain 128 bytes on either side of the
270 // instruction pointer.
271 memcpy(memory + kOffset, instructions, sizeof(instructions));
272
273 // Now execute the instructions, which should crash.
274 typedef void (*void_function)(void);
275 void_function memory_function =
276 reinterpret_cast<void_function>(memory + kOffset);
277 ASSERT_DEATH(memory_function(), "");
278
279 // free the memory.
280 VirtualFree(memory, 0, MEM_RELEASE);
281
282 // Verify that the resulting minidump contains the memory around the IP
283 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
284 ASSERT_FALSE(minidump_filename_wide.empty());
285 string minidump_filename;
286 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
287 &minidump_filename));
288
289 // Read the minidump. Locate the exception record and the
290 // memory list, and then ensure that there is a memory region
291 // in the memory list that covers the instruction pointer from
292 // the exception record.
293 {
294 Minidump minidump(minidump_filename);
295 ASSERT_TRUE(minidump.Read());
296
297 MinidumpException* exception = minidump.GetException();
298 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
299 ASSERT_TRUE(exception);
300 ASSERT_TRUE(memory_list);
301 ASSERT_LT((unsigned)0, memory_list->region_count());
302
303 MinidumpContext* context = exception->GetContext();
304 ASSERT_TRUE(context);
305
306 u_int64_t instruction_pointer;
307 switch (context->GetContextCPU()) {
308 case MD_CONTEXT_X86:
309 instruction_pointer = context->GetContextX86()->eip;
310 break;
311 case MD_CONTEXT_AMD64:
312 instruction_pointer = context->GetContextAMD64()->rip;
313 break;
314 default:
315 FAIL() << "Unknown context CPU: " << context->GetContextCPU();
316 break;
317 }
318
319 MinidumpMemoryRegion* region =
320 memory_list->GetMemoryRegionForAddress(instruction_pointer);
321 ASSERT_TRUE(region);
322
323 EXPECT_EQ(kMemorySize, region->GetSize());
324 const u_int8_t* bytes = region->GetMemory();
325 ASSERT_TRUE(bytes);
326
327 u_int8_t prefix_bytes[kOffset];
328 u_int8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
329 memset(prefix_bytes, 0, sizeof(prefix_bytes));
330 memset(suffix_bytes, 0, sizeof(suffix_bytes));
331 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
332 EXPECT_TRUE(memcmp(bytes + kOffset, instructions,
333 sizeof(instructions)) == 0);
334 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
335 suffix_bytes, sizeof(suffix_bytes)) == 0);
336 }
337
338 DeleteFileW(minidump_filename_wide.c_str());
339}
340
341TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) {
342 ASSERT_TRUE(DoesPathExist(temp_path_));
343 google_breakpad::ExceptionHandler *exc =
344 new google_breakpad::ExceptionHandler(
345 temp_path_, NULL, NULL, NULL,
346 google_breakpad::ExceptionHandler::HANDLER_ALL);
347
348 SYSTEM_INFO sSysInfo; // Useful information about the system
349 GetSystemInfo(&sSysInfo); // Initialize the structure.
350
351 const u_int32_t kMemorySize = 256; // bytes
352 const DWORD kPageSize = sSysInfo.dwPageSize;
353 const int kOffset = 0;
354 // This crashes with SIGILL on x86/x86-64/arm.
355 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
356 // Get some executable memory. Specifically, reserve two pages,
357 // but only commit the second.
358 char* all_memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
359 kPageSize * 2,
360 MEM_RESERVE,
361 PAGE_NOACCESS));
362 ASSERT_TRUE(all_memory);
363 char* memory = all_memory + kPageSize;
364 ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
365 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
366
367 // Write some instructions that will crash. Put them
368 // in the middle of the block of memory, because the
369 // minidump should contain 128 bytes on either side of the
370 // instruction pointer.
371 memcpy(memory + kOffset, instructions, sizeof(instructions));
372
373 // Now execute the instructions, which should crash.
374 typedef void (*void_function)(void);
375 void_function memory_function =
376 reinterpret_cast<void_function>(memory + kOffset);
377 ASSERT_DEATH(memory_function(), "");
378
379 // free the memory.
380 VirtualFree(memory, 0, MEM_RELEASE);
381
382 // Verify that the resulting minidump contains the memory around the IP
383 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
384 ASSERT_FALSE(minidump_filename_wide.empty());
385 string minidump_filename;
386 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
387 &minidump_filename));
388
389 // Read the minidump. Locate the exception record and the
390 // memory list, and then ensure that there is a memory region
391 // in the memory list that covers the instruction pointer from
392 // the exception record.
393 {
394 Minidump minidump(minidump_filename);
395 ASSERT_TRUE(minidump.Read());
396
397 MinidumpException* exception = minidump.GetException();
398 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
399 ASSERT_TRUE(exception);
400 ASSERT_TRUE(memory_list);
401 ASSERT_LT((unsigned)0, memory_list->region_count());
402
403 MinidumpContext* context = exception->GetContext();
404 ASSERT_TRUE(context);
405
406 u_int64_t instruction_pointer;
407 switch (context->GetContextCPU()) {
408 case MD_CONTEXT_X86:
409 instruction_pointer = context->GetContextX86()->eip;
410 break;
411 case MD_CONTEXT_AMD64:
412 instruction_pointer = context->GetContextAMD64()->rip;
413 break;
414 default:
415 FAIL() << "Unknown context CPU: " << context->GetContextCPU();
416 break;
417 }
418
419 MinidumpMemoryRegion* region =
420 memory_list->GetMemoryRegionForAddress(instruction_pointer);
421 ASSERT_TRUE(region);
422
423 EXPECT_EQ(kMemorySize / 2, region->GetSize());
424 const u_int8_t* bytes = region->GetMemory();
425 ASSERT_TRUE(bytes);
426
427 u_int8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
428 memset(suffix_bytes, 0, sizeof(suffix_bytes));
429 EXPECT_TRUE(memcmp(bytes + kOffset,
430 instructions, sizeof(instructions)) == 0);
431 EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
432 suffix_bytes, sizeof(suffix_bytes)) == 0);
433 }
434
435 DeleteFileW(minidump_filename_wide.c_str());
436}
437
438TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) {
439 ASSERT_TRUE(DoesPathExist(temp_path_));
440 google_breakpad::ExceptionHandler *exc =
441 new google_breakpad::ExceptionHandler(
442 temp_path_, NULL, NULL, NULL,
443 google_breakpad::ExceptionHandler::HANDLER_ALL);
444
445 SYSTEM_INFO sSysInfo; // Useful information about the system
446 GetSystemInfo(&sSysInfo); // Initialize the structure.
447
448 const DWORD kPageSize = sSysInfo.dwPageSize;
449 // This crashes with SIGILL on x86/x86-64/arm.
450 const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
451 const int kOffset = kPageSize - sizeof(instructions);
452 // Get some executable memory. Specifically, reserve two pages,
453 // but only commit the first.
454 char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
455 kPageSize * 2,
456 MEM_RESERVE,
457 PAGE_NOACCESS));
458 ASSERT_TRUE(memory);
459 ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
460 MEM_COMMIT, PAGE_EXECUTE_READWRITE));
461
462 // Write some instructions that will crash.
463 memcpy(memory + kOffset, instructions, sizeof(instructions));
464
465 // Now execute the instructions, which should crash.
466 typedef void (*void_function)(void);
467 void_function memory_function =
468 reinterpret_cast<void_function>(memory + kOffset);
469 ASSERT_DEATH(memory_function(), "");
470
471 // free the memory.
472 VirtualFree(memory, 0, MEM_RELEASE);
473
474 // Verify that the resulting minidump contains the memory around the IP
475 wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
476 ASSERT_FALSE(minidump_filename_wide.empty());
477 string minidump_filename;
478 ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
479 &minidump_filename));
480
481 // Read the minidump. Locate the exception record and the
482 // memory list, and then ensure that there is a memory region
483 // in the memory list that covers the instruction pointer from
484 // the exception record.
485 {
486 Minidump minidump(minidump_filename);
487 ASSERT_TRUE(minidump.Read());
488
489 MinidumpException* exception = minidump.GetException();
490 MinidumpMemoryList* memory_list = minidump.GetMemoryList();
491 ASSERT_TRUE(exception);
492 ASSERT_TRUE(memory_list);
493 ASSERT_LT((unsigned)0, memory_list->region_count());
494
495 MinidumpContext* context = exception->GetContext();
496 ASSERT_TRUE(context);
497
498 u_int64_t instruction_pointer;
499 switch (context->GetContextCPU()) {
500 case MD_CONTEXT_X86:
501 instruction_pointer = context->GetContextX86()->eip;
502 break;
503 case MD_CONTEXT_AMD64:
504 instruction_pointer = context->GetContextAMD64()->rip;
505 break;
506 default:
507 FAIL() << "Unknown context CPU: " << context->GetContextCPU();
508 break;
509 }
510
511 MinidumpMemoryRegion* region =
512 memory_list->GetMemoryRegionForAddress(instruction_pointer);
513 ASSERT_TRUE(region);
514
515 const size_t kPrefixSize = 128; // bytes
516 EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
517 const u_int8_t* bytes = region->GetMemory();
518 ASSERT_TRUE(bytes);
519
520 u_int8_t prefix_bytes[kPrefixSize];
521 memset(prefix_bytes, 0, sizeof(prefix_bytes));
522 EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
523 EXPECT_TRUE(memcmp(bytes + kPrefixSize,
524 instructions, sizeof(instructions)) == 0);
525 }
526
527 DeleteFileW(minidump_filename_wide.c_str());
528}
529
530} // namespace