xref: /aosp_15_r20/external/google-breakpad/src/client/windows/unittests/exception_handler_death_test.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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