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 "client/windows/unittests/exception_handler_test.h"
31 
32 #include <windows.h>
33 #include <dbghelp.h>
34 #include <strsafe.h>
35 #include <objbase.h>
36 #include <shellapi.h>
37 
38 #include <string>
39 
40 #include "breakpad_googletest_includes.h"
41 #include "client/windows/crash_generation/crash_generation_server.h"
42 #include "client/windows/handler/exception_handler.h"
43 #include "client/windows/unittests/dump_analysis.h"  // NOLINT
44 #include "common/windows/string_utils-inl.h"
45 #include "google_breakpad/processor/minidump.h"
46 
47 namespace testing {
48 
DisableExceptionHandlerInScope()49 DisableExceptionHandlerInScope::DisableExceptionHandlerInScope() {
50   catch_exceptions_ = GTEST_FLAG(catch_exceptions);
51   GTEST_FLAG(catch_exceptions) = false;
52 }
53 
~DisableExceptionHandlerInScope()54 DisableExceptionHandlerInScope::~DisableExceptionHandlerInScope() {
55   GTEST_FLAG(catch_exceptions) = catch_exceptions_;
56 }
57 
58 }  // namespace testing
59 
60 namespace {
61 
62 using std::wstring;
63 using namespace google_breakpad;
64 
65 const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
66 const char kSuccessIndicator[] = "success";
67 const char kFailureIndicator[] = "failure";
68 
69 const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
70     MiniDumpWithFullMemory |  // Full memory from process.
71     MiniDumpWithProcessThreadData |  // Get PEB and TEB.
72     MiniDumpWithHandleData);  // Get all handle information.
73 
74 class ExceptionHandlerTest : public ::testing::Test {
75  protected:
76   // Member variable for each test that they can use
77   // for temporary storage.
78   TCHAR temp_path_[MAX_PATH];
79 
80   // Actually constructs a temp path name.
81   virtual void SetUp();
82 
83   // Deletes temporary files.
84   virtual void TearDown();
85 
86   void DoCrashInvalidParameter();
87   void DoCrashPureVirtualCall();
88 
89   // Utility function to test for a path's existence.
90   static BOOL DoesPathExist(const TCHAR *path_name);
91 
92   // Client callback.
93   static void ClientDumpCallback(
94       void *dump_context,
95       const google_breakpad::ClientInfo *client_info,
96       const std::wstring *dump_path);
97 
98   static bool DumpCallback(const wchar_t* dump_path,
99                            const wchar_t* minidump_id,
100                            void* context,
101                            EXCEPTION_POINTERS* exinfo,
102                            MDRawAssertionInfo* assertion,
103                            bool succeeded);
104 
105   static std::wstring dump_file;
106   static std::wstring full_dump_file;
107 };
108 
109 std::wstring ExceptionHandlerTest::dump_file;
110 std::wstring ExceptionHandlerTest::full_dump_file;
111 
SetUp()112 void ExceptionHandlerTest::SetUp() {
113   const ::testing::TestInfo* const test_info =
114     ::testing::UnitTest::GetInstance()->current_test_info();
115   TCHAR temp_path[MAX_PATH] = { '\0' };
116   TCHAR test_name_wide[MAX_PATH] = { '\0' };
117   // We want the temporary directory to be what the OS returns
118   // to us, + the test case name.
119   GetTempPath(MAX_PATH, temp_path);
120   // THe test case name is exposed to use as a c-style string,
121   // But we might be working in UNICODE here on Windows.
122   int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
123                                   strlen(test_info->name()),
124                                   test_name_wide,
125                                   MAX_PATH);
126   if (!dwRet) {
127     assert(false);
128   }
129   StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
130   CreateDirectory(temp_path_, NULL);
131 }
132 
TearDown()133 void ExceptionHandlerTest::TearDown() {
134   if (!dump_file.empty()) {
135     ::DeleteFile(dump_file.c_str());
136     dump_file = L"";
137   }
138   if (!full_dump_file.empty()) {
139     ::DeleteFile(full_dump_file.c_str());
140     full_dump_file = L"";
141   }
142 }
143 
DoesPathExist(const TCHAR * path_name)144 BOOL ExceptionHandlerTest::DoesPathExist(const TCHAR *path_name) {
145   DWORD flags = GetFileAttributes(path_name);
146   if (flags == INVALID_FILE_ATTRIBUTES) {
147     return FALSE;
148   }
149   return TRUE;
150 }
151 
152 // static
ClientDumpCallback(void * dump_context,const google_breakpad::ClientInfo * client_info,const wstring * dump_path)153 void ExceptionHandlerTest::ClientDumpCallback(
154     void *dump_context,
155     const google_breakpad::ClientInfo *client_info,
156     const wstring *dump_path) {
157   dump_file = *dump_path;
158   // Create the full dump file name from the dump path.
159   full_dump_file = dump_file.substr(0, dump_file.length() - 4) + L"-full.dmp";
160 }
161 
162 // static
DumpCallback(const wchar_t * dump_path,const wchar_t * minidump_id,void * context,EXCEPTION_POINTERS * exinfo,MDRawAssertionInfo * assertion,bool succeeded)163 bool ExceptionHandlerTest::DumpCallback(const wchar_t* dump_path,
164                     const wchar_t* minidump_id,
165                     void* context,
166                     EXCEPTION_POINTERS* exinfo,
167                     MDRawAssertionInfo* assertion,
168                     bool succeeded) {
169   dump_file = dump_path;
170   dump_file += L"\\";
171   dump_file += minidump_id;
172   dump_file += L".dmp";
173     return succeeded;
174 }
175 
DoCrashInvalidParameter()176 void ExceptionHandlerTest::DoCrashInvalidParameter() {
177   google_breakpad::ExceptionHandler *exc =
178       new google_breakpad::ExceptionHandler(
179           temp_path_, NULL, NULL, NULL,
180           google_breakpad::ExceptionHandler::HANDLER_INVALID_PARAMETER,
181           kFullDumpType, kPipeName, NULL);
182 
183   // Disable the message box for assertions
184   _CrtSetReportMode(_CRT_ASSERT, 0);
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   printf(NULL);
191 }
192 
193 
194 struct PureVirtualCallBase {
PureVirtualCallBase__anon5132e4040111::PureVirtualCallBase195   PureVirtualCallBase() {
196     // We have to reinterpret so the linker doesn't get confused because the
197     // method isn't defined.
198     reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
199   }
~PureVirtualCallBase__anon5132e4040111::PureVirtualCallBase200   virtual ~PureVirtualCallBase() {}
201   virtual void PureFunction() const = 0;
202 };
203 struct PureVirtualCall : public PureVirtualCallBase {
PureVirtualCall__anon5132e4040111::PureVirtualCall204   PureVirtualCall() { PureFunction(); }
PureFunction__anon5132e4040111::PureVirtualCall205   virtual void PureFunction() const {}
206 };
207 
DoCrashPureVirtualCall()208 void ExceptionHandlerTest::DoCrashPureVirtualCall() {
209   google_breakpad::ExceptionHandler *exc =
210       new google_breakpad::ExceptionHandler(
211           temp_path_, NULL, NULL, NULL,
212           google_breakpad::ExceptionHandler::HANDLER_PURECALL,
213           kFullDumpType, kPipeName, NULL);
214 
215   // Disable the message box for assertions
216   _CrtSetReportMode(_CRT_ASSERT, 0);
217 
218   // Although this is executing in the child process of the death test,
219   // if it's not true we'll still get an error rather than the crash
220   // being expected.
221   ASSERT_TRUE(exc->IsOutOfProcess());
222 
223   // Create a new frame to ensure PureVirtualCall is not optimized to some
224   // other line in this function.
225   {
226     PureVirtualCall instance;
227   }
228 }
229 
230 // This test validates that the minidump is written correctly.
TEST_F(ExceptionHandlerTest,InvalidParameterMiniDumpTest)231 TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) {
232   ASSERT_TRUE(DoesPathExist(temp_path_));
233 
234   // Call with a bad argument
235   ASSERT_TRUE(DoesPathExist(temp_path_));
236   wstring dump_path(temp_path_);
237   google_breakpad::CrashGenerationServer server(
238       kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL,
239       NULL, true, &dump_path);
240 
241   ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
242 
243   // This HAS to be EXPECT_, because when this test case is executed in the
244   // child process, the server registration will fail due to the named pipe
245   // being the same.
246   EXPECT_TRUE(server.Start());
247   EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), "");
248   ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
249   ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
250 
251   // Verify the dump for infos.
252   DumpAnalysis mini(dump_file);
253   DumpAnalysis full(full_dump_file);
254 
255   // The dump should have all of these streams.
256   EXPECT_TRUE(mini.HasStream(ThreadListStream));
257   EXPECT_TRUE(full.HasStream(ThreadListStream));
258   EXPECT_TRUE(mini.HasStream(ModuleListStream));
259   EXPECT_TRUE(full.HasStream(ModuleListStream));
260   EXPECT_TRUE(mini.HasStream(ExceptionStream));
261   EXPECT_TRUE(full.HasStream(ExceptionStream));
262   EXPECT_TRUE(mini.HasStream(SystemInfoStream));
263   EXPECT_TRUE(full.HasStream(SystemInfoStream));
264   EXPECT_TRUE(mini.HasStream(MiscInfoStream));
265   EXPECT_TRUE(full.HasStream(MiscInfoStream));
266   EXPECT_TRUE(mini.HasStream(HandleDataStream));
267   EXPECT_TRUE(full.HasStream(HandleDataStream));
268 
269   // We expect PEB and TEBs in this dump.
270   EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
271   EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
272 
273   // Minidump should have a memory listing, but no 64-bit memory.
274   EXPECT_TRUE(mini.HasStream(MemoryListStream));
275   EXPECT_FALSE(mini.HasStream(Memory64ListStream));
276 
277   EXPECT_FALSE(full.HasStream(MemoryListStream));
278   EXPECT_TRUE(full.HasStream(Memory64ListStream));
279 
280   // This is the only place we don't use OR because we want both not
281   // to have the streams.
282   EXPECT_FALSE(mini.HasStream(ThreadExListStream));
283   EXPECT_FALSE(full.HasStream(ThreadExListStream));
284   EXPECT_FALSE(mini.HasStream(CommentStreamA));
285   EXPECT_FALSE(full.HasStream(CommentStreamA));
286   EXPECT_FALSE(mini.HasStream(CommentStreamW));
287   EXPECT_FALSE(full.HasStream(CommentStreamW));
288   EXPECT_FALSE(mini.HasStream(FunctionTableStream));
289   EXPECT_FALSE(full.HasStream(FunctionTableStream));
290   EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
291   EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
292   EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
293   EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
294   EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
295   EXPECT_FALSE(full.HasStream(HandleOperationListStream));
296   EXPECT_FALSE(mini.HasStream(TokenStream));
297   EXPECT_FALSE(full.HasStream(TokenStream));
298 }
299 
300 
301 // This test validates that the minidump is written correctly.
TEST_F(ExceptionHandlerTest,PureVirtualCallMiniDumpTest)302 TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
303   ASSERT_TRUE(DoesPathExist(temp_path_));
304 
305   // Call with a bad argument
306   ASSERT_TRUE(DoesPathExist(temp_path_));
307   wstring dump_path(temp_path_);
308   google_breakpad::CrashGenerationServer server(
309       kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL,
310       NULL, true, &dump_path);
311 
312   ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
313 
314   // This HAS to be EXPECT_, because when this test case is executed in the
315   // child process, the server registration will fail due to the named pipe
316   // being the same.
317   EXPECT_TRUE(server.Start());
318   EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
319   ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
320   ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
321 
322   // Verify the dump for infos.
323   DumpAnalysis mini(dump_file);
324   DumpAnalysis full(full_dump_file);
325 
326   // The dump should have all of these streams.
327   EXPECT_TRUE(mini.HasStream(ThreadListStream));
328   EXPECT_TRUE(full.HasStream(ThreadListStream));
329   EXPECT_TRUE(mini.HasStream(ModuleListStream));
330   EXPECT_TRUE(full.HasStream(ModuleListStream));
331   EXPECT_TRUE(mini.HasStream(ExceptionStream));
332   EXPECT_TRUE(full.HasStream(ExceptionStream));
333   EXPECT_TRUE(mini.HasStream(SystemInfoStream));
334   EXPECT_TRUE(full.HasStream(SystemInfoStream));
335   EXPECT_TRUE(mini.HasStream(MiscInfoStream));
336   EXPECT_TRUE(full.HasStream(MiscInfoStream));
337   EXPECT_TRUE(mini.HasStream(HandleDataStream));
338   EXPECT_TRUE(full.HasStream(HandleDataStream));
339 
340   // We expect PEB and TEBs in this dump.
341   EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
342   EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
343 
344   // Minidump should have a memory listing, but no 64-bit memory.
345   EXPECT_TRUE(mini.HasStream(MemoryListStream));
346   EXPECT_FALSE(mini.HasStream(Memory64ListStream));
347 
348   EXPECT_FALSE(full.HasStream(MemoryListStream));
349   EXPECT_TRUE(full.HasStream(Memory64ListStream));
350 
351   // This is the only place we don't use OR because we want both not
352   // to have the streams.
353   EXPECT_FALSE(mini.HasStream(ThreadExListStream));
354   EXPECT_FALSE(full.HasStream(ThreadExListStream));
355   EXPECT_FALSE(mini.HasStream(CommentStreamA));
356   EXPECT_FALSE(full.HasStream(CommentStreamA));
357   EXPECT_FALSE(mini.HasStream(CommentStreamW));
358   EXPECT_FALSE(full.HasStream(CommentStreamW));
359   EXPECT_FALSE(mini.HasStream(FunctionTableStream));
360   EXPECT_FALSE(full.HasStream(FunctionTableStream));
361   EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
362   EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
363   EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
364   EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
365   EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
366   EXPECT_FALSE(full.HasStream(HandleOperationListStream));
367   EXPECT_FALSE(mini.HasStream(TokenStream));
368   EXPECT_FALSE(full.HasStream(TokenStream));
369 }
370 
371 // Test that writing a minidump produces a valid minidump containing
372 // some expected structures.
TEST_F(ExceptionHandlerTest,WriteMinidumpTest)373 TEST_F(ExceptionHandlerTest, WriteMinidumpTest) {
374   ExceptionHandler handler(temp_path_,
375                            NULL,
376                            DumpCallback,
377                            NULL,
378                            ExceptionHandler::HANDLER_ALL);
379 
380   // Disable GTest SEH handler
381   testing::DisableExceptionHandlerInScope disable_exception_handler;
382 
383   ASSERT_TRUE(handler.WriteMinidump());
384   ASSERT_FALSE(dump_file.empty());
385 
386   string minidump_filename;
387   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
388                                                 &minidump_filename));
389 
390   // Read the minidump and verify some info.
391   Minidump minidump(minidump_filename);
392   ASSERT_TRUE(minidump.Read());
393   // TODO(ted): more comprehensive tests...
394 }
395 
396 // Test that an additional memory region can be included in the minidump.
TEST_F(ExceptionHandlerTest,AdditionalMemory)397 TEST_F(ExceptionHandlerTest, AdditionalMemory) {
398   SYSTEM_INFO si;
399   GetSystemInfo(&si);
400   const uint32_t kMemorySize = si.dwPageSize;
401 
402   // Get some heap memory.
403   uint8_t* memory = new uint8_t[kMemorySize];
404   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
405   ASSERT_TRUE(memory);
406 
407   // Stick some data into the memory so the contents can be verified.
408   for (uint32_t i = 0; i < kMemorySize; ++i) {
409     memory[i] = i % 255;
410   }
411 
412   ExceptionHandler handler(temp_path_,
413                            NULL,
414                            DumpCallback,
415                            NULL,
416                            ExceptionHandler::HANDLER_ALL);
417 
418   // Disable GTest SEH handler
419   testing::DisableExceptionHandlerInScope disable_exception_handler;
420 
421   // Add the memory region to the list of memory to be included.
422   handler.RegisterAppMemory(memory, kMemorySize);
423   ASSERT_TRUE(handler.WriteMinidump());
424   ASSERT_FALSE(dump_file.empty());
425 
426   string minidump_filename;
427   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
428                                                 &minidump_filename));
429 
430   // Read the minidump. Ensure that the memory region is present
431   Minidump minidump(minidump_filename);
432   ASSERT_TRUE(minidump.Read());
433 
434   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
435   ASSERT_TRUE(dump_memory_list);
436   const MinidumpMemoryRegion* region =
437     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
438   ASSERT_TRUE(region);
439 
440   EXPECT_EQ(kMemoryAddress, region->GetBase());
441   EXPECT_EQ(kMemorySize, region->GetSize());
442 
443   // Verify memory contents.
444   EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
445 
446   delete[] memory;
447 }
448 
449 // Test that a memory region that was previously registered
450 // can be unregistered.
TEST_F(ExceptionHandlerTest,AdditionalMemoryRemove)451 TEST_F(ExceptionHandlerTest, AdditionalMemoryRemove) {
452   SYSTEM_INFO si;
453   GetSystemInfo(&si);
454   const uint32_t kMemorySize = si.dwPageSize;
455 
456   // Get some heap memory.
457   uint8_t* memory = new uint8_t[kMemorySize];
458   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
459   ASSERT_TRUE(memory);
460 
461   // Stick some data into the memory so the contents can be verified.
462   for (uint32_t i = 0; i < kMemorySize; ++i) {
463     memory[i] = i % 255;
464   }
465 
466   ExceptionHandler handler(temp_path_,
467                            NULL,
468                            DumpCallback,
469                            NULL,
470                            ExceptionHandler::HANDLER_ALL);
471 
472   // Disable GTest SEH handler
473   testing::DisableExceptionHandlerInScope disable_exception_handler;
474 
475   // Add the memory region to the list of memory to be included.
476   handler.RegisterAppMemory(memory, kMemorySize);
477 
478   // ...and then remove it
479   handler.UnregisterAppMemory(memory);
480 
481   ASSERT_TRUE(handler.WriteMinidump());
482   ASSERT_FALSE(dump_file.empty());
483 
484   string minidump_filename;
485   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
486                                                 &minidump_filename));
487 
488   // Read the minidump. Ensure that the memory region is not present.
489   Minidump minidump(minidump_filename);
490   ASSERT_TRUE(minidump.Read());
491 
492   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
493   ASSERT_TRUE(dump_memory_list);
494   const MinidumpMemoryRegion* region =
495     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
496   EXPECT_FALSE(region);
497 
498   delete[] memory;
499 }
500 
501 }  // namespace
502