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