• 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                                   static_cast<int>(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__anon655245ce0111::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__anon655245ce0111::PureVirtualCallBase260   virtual ~PureVirtualCallBase() {}
261   virtual void PureFunction() const = 0;
262 };
263 struct PureVirtualCall : public PureVirtualCallBase {
PureVirtualCall__anon655245ce0111::PureVirtualCall264   PureVirtualCall() { PureFunction(); }
PureFunction__anon655245ce0111::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 size_t extension_length = sizeof(extension) / sizeof(extension[0]) - 1;
297     const size_t 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 at least 128 bytes on either
361   // side of the instruction pointer from 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_LE(kMemorySize, region->GetSize());
383     const uint8_t* bytes = region->GetMemory();
384     ASSERT_TRUE(bytes);
385 
386     uint64_t ip_offset = instruction_pointer - region->GetBase();
387     EXPECT_GE(region->GetSize() - kOffset, ip_offset);
388     EXPECT_LE(kOffset, ip_offset);
389 
390     uint8_t prefix_bytes[kOffset];
391     uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
392     memset(prefix_bytes, 0, sizeof(prefix_bytes));
393     memset(suffix_bytes, 0, sizeof(suffix_bytes));
394     EXPECT_EQ(0, memcmp(bytes + ip_offset - kOffset, prefix_bytes,
395                         sizeof(prefix_bytes)));
396     EXPECT_EQ(0, memcmp(bytes + ip_offset, instructions, sizeof(instructions)));
397     EXPECT_EQ(0, memcmp(bytes + ip_offset + sizeof(instructions), suffix_bytes,
398                         sizeof(suffix_bytes)));
399   }
400 
401   DeleteFileW(minidump_filename_wide.c_str());
402 }
403 
TEST_F(ExceptionHandlerDeathTest,InstructionPointerMemoryMinBound)404 TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) {
405   ASSERT_TRUE(DoesPathExist(temp_path_));
406   scoped_ptr<google_breakpad::ExceptionHandler> exc(
407       new google_breakpad::ExceptionHandler(
408           temp_path_,
409           NULL,
410           NULL,
411           NULL,
412           google_breakpad::ExceptionHandler::HANDLER_ALL));
413 
414   // Disable GTest SEH handler
415   testing::DisableExceptionHandlerInScope disable_exception_handler;
416 
417   SYSTEM_INFO sSysInfo;         // Useful information about the system
418   GetSystemInfo(&sSysInfo);     // Initialize the structure.
419 
420   const uint32_t kMemorySize = 256;  // bytes
421   const DWORD kPageSize = sSysInfo.dwPageSize;
422   const int kOffset = 0;
423   // This crashes with SIGILL on x86/x86-64/arm.
424   const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
425   // Get some executable memory. Specifically, reserve two pages,
426   // but only commit the second.
427   char* all_memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
428                                                           kPageSize * 2,
429                                                           MEM_RESERVE,
430                                                           PAGE_NOACCESS));
431   ASSERT_TRUE(all_memory);
432   char* memory = all_memory + kPageSize;
433   ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
434                            MEM_COMMIT, PAGE_EXECUTE_READWRITE));
435 
436   // Write some instructions that will crash. Put them
437   // in the middle of the block of memory, because the
438   // minidump should contain 128 bytes on either side of the
439   // instruction pointer.
440   memcpy(memory + kOffset, instructions, sizeof(instructions));
441 
442   // Now execute the instructions, which should crash.
443   typedef void (*void_function)(void);
444   void_function memory_function =
445       reinterpret_cast<void_function>(memory + kOffset);
446   ASSERT_DEATH(memory_function(), "");
447 
448   // free the memory.
449   VirtualFree(memory, 0, MEM_RELEASE);
450 
451   // Verify that the resulting minidump contains the memory around the IP
452   wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
453   ASSERT_FALSE(minidump_filename_wide.empty());
454   string minidump_filename;
455   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
456                                                 &minidump_filename));
457 
458   // Read the minidump. Locate the exception record and the
459   // memory list, and then ensure that there is a memory region
460   // in the memory list that covers the instruction pointer from
461   // the exception record.
462   {
463     Minidump minidump(minidump_filename);
464     ASSERT_TRUE(minidump.Read());
465 
466     MinidumpException* exception = minidump.GetException();
467     MinidumpMemoryList* memory_list = minidump.GetMemoryList();
468     ASSERT_TRUE(exception);
469     ASSERT_TRUE(memory_list);
470     ASSERT_LT((unsigned)0, memory_list->region_count());
471 
472     MinidumpContext* context = exception->GetContext();
473     ASSERT_TRUE(context);
474 
475     uint64_t instruction_pointer;
476     ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
477 
478     MinidumpMemoryRegion* region =
479         memory_list->GetMemoryRegionForAddress(instruction_pointer);
480     ASSERT_TRUE(region);
481 
482     EXPECT_EQ(kMemorySize / 2, region->GetSize());
483     const uint8_t* bytes = region->GetMemory();
484     ASSERT_TRUE(bytes);
485 
486     uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
487     memset(suffix_bytes, 0, sizeof(suffix_bytes));
488     EXPECT_TRUE(memcmp(bytes + kOffset,
489                        instructions, sizeof(instructions)) == 0);
490     EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
491                        suffix_bytes, sizeof(suffix_bytes)) == 0);
492   }
493 
494   DeleteFileW(minidump_filename_wide.c_str());
495 }
496 
TEST_F(ExceptionHandlerDeathTest,InstructionPointerMemoryMaxBound)497 TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) {
498   ASSERT_TRUE(DoesPathExist(temp_path_));
499   scoped_ptr<google_breakpad::ExceptionHandler> exc(
500       new google_breakpad::ExceptionHandler(
501           temp_path_,
502           NULL,
503           NULL,
504           NULL,
505           google_breakpad::ExceptionHandler::HANDLER_ALL));
506 
507   // Disable GTest SEH handler
508   testing::DisableExceptionHandlerInScope disable_exception_handler;
509 
510   SYSTEM_INFO sSysInfo;         // Useful information about the system
511   GetSystemInfo(&sSysInfo);     // Initialize the structure.
512 
513   const DWORD kPageSize = sSysInfo.dwPageSize;
514   // This crashes with SIGILL on x86/x86-64/arm.
515   const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
516   const int kOffset = kPageSize - sizeof(instructions);
517   // Get some executable memory. Specifically, reserve two pages,
518   // but only commit the first.
519   char* memory = reinterpret_cast<char*>(VirtualAlloc(NULL,
520                                                       kPageSize * 2,
521                                                       MEM_RESERVE,
522                                                       PAGE_NOACCESS));
523   ASSERT_TRUE(memory);
524   ASSERT_TRUE(VirtualAlloc(memory, kPageSize,
525                            MEM_COMMIT, PAGE_EXECUTE_READWRITE));
526 
527   // Write some instructions that will crash.
528   memcpy(memory + kOffset, instructions, sizeof(instructions));
529 
530   // Now execute the instructions, which should crash.
531   typedef void (*void_function)(void);
532   void_function memory_function =
533       reinterpret_cast<void_function>(memory + kOffset);
534   ASSERT_DEATH(memory_function(), "");
535 
536   // free the memory.
537   VirtualFree(memory, 0, MEM_RELEASE);
538 
539   // Verify that the resulting minidump contains the memory around the IP
540   wstring minidump_filename_wide = find_minidump_in_directory(temp_path_);
541   ASSERT_FALSE(minidump_filename_wide.empty());
542   string minidump_filename;
543   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide,
544                                                 &minidump_filename));
545 
546   // Read the minidump. Locate the exception record and the
547   // memory list, and then ensure that there is a memory region
548   // in the memory list that covers the instruction pointer from
549   // the exception record.
550   {
551     Minidump minidump(minidump_filename);
552     ASSERT_TRUE(minidump.Read());
553 
554     MinidumpException* exception = minidump.GetException();
555     MinidumpMemoryList* memory_list = minidump.GetMemoryList();
556     ASSERT_TRUE(exception);
557     ASSERT_TRUE(memory_list);
558     ASSERT_LT((unsigned)0, memory_list->region_count());
559 
560     MinidumpContext* context = exception->GetContext();
561     ASSERT_TRUE(context);
562 
563     uint64_t instruction_pointer;
564     ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer));
565 
566     MinidumpMemoryRegion* region =
567         memory_list->GetMemoryRegionForAddress(instruction_pointer);
568     ASSERT_TRUE(region);
569 
570     const size_t kPrefixSize = 128;  // bytes
571     EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
572     const uint8_t* bytes = region->GetMemory();
573     ASSERT_TRUE(bytes);
574 
575     uint8_t prefix_bytes[kPrefixSize];
576     memset(prefix_bytes, 0, sizeof(prefix_bytes));
577     EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)));
578     EXPECT_EQ(0, memcmp(bytes + kPrefixSize,
579                         instructions, sizeof(instructions)));
580   }
581 
582   DeleteFileW(minidump_filename_wide.c_str());
583 }
584 
585 #endif  // !ADDRESS_SANITIZER
586 
587 }  // namespace
588