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