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                                   static_cast<int>(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   ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
251 
252   // Verify the dump for infos.
253   DumpAnalysis mini(dump_file);
254   DumpAnalysis full(full_dump_file);
255 
256   // The dump should have all of these streams.
257   EXPECT_TRUE(mini.HasStream(ThreadListStream));
258   EXPECT_TRUE(full.HasStream(ThreadListStream));
259   EXPECT_TRUE(mini.HasStream(ModuleListStream));
260   EXPECT_TRUE(full.HasStream(ModuleListStream));
261   EXPECT_TRUE(mini.HasStream(ExceptionStream));
262   EXPECT_TRUE(full.HasStream(ExceptionStream));
263   EXPECT_TRUE(mini.HasStream(SystemInfoStream));
264   EXPECT_TRUE(full.HasStream(SystemInfoStream));
265   EXPECT_TRUE(mini.HasStream(MiscInfoStream));
266   EXPECT_TRUE(full.HasStream(MiscInfoStream));
267   EXPECT_TRUE(mini.HasStream(HandleDataStream));
268   EXPECT_TRUE(full.HasStream(HandleDataStream));
269 
270   // We expect PEB and TEBs in this dump.
271   EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
272   EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
273 
274   // Minidump should have a memory listing, but no 64-bit memory.
275   EXPECT_TRUE(mini.HasStream(MemoryListStream));
276   EXPECT_FALSE(mini.HasStream(Memory64ListStream));
277 
278   EXPECT_FALSE(full.HasStream(MemoryListStream));
279   EXPECT_TRUE(full.HasStream(Memory64ListStream));
280 
281   // This is the only place we don't use OR because we want both not
282   // to have the streams.
283   EXPECT_FALSE(mini.HasStream(ThreadExListStream));
284   EXPECT_FALSE(full.HasStream(ThreadExListStream));
285   EXPECT_FALSE(mini.HasStream(CommentStreamA));
286   EXPECT_FALSE(full.HasStream(CommentStreamA));
287   EXPECT_FALSE(mini.HasStream(CommentStreamW));
288   EXPECT_FALSE(full.HasStream(CommentStreamW));
289   EXPECT_FALSE(mini.HasStream(FunctionTableStream));
290   EXPECT_FALSE(full.HasStream(FunctionTableStream));
291   EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
292   EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
293   EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
294   EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
295   EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
296   EXPECT_FALSE(full.HasStream(HandleOperationListStream));
297   EXPECT_FALSE(mini.HasStream(TokenStream));
298   EXPECT_FALSE(full.HasStream(TokenStream));
299 }
300 
301 
302 // This test validates that the minidump is written correctly.
TEST_F(ExceptionHandlerTest,PureVirtualCallMiniDumpTest)303 TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
304   ASSERT_TRUE(DoesPathExist(temp_path_));
305 
306   // Call with a bad argument
307   ASSERT_TRUE(DoesPathExist(temp_path_));
308   wstring dump_path(temp_path_);
309   google_breakpad::CrashGenerationServer server(
310       kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL,
311       NULL, true, &dump_path);
312 
313   ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
314 
315   // This HAS to be EXPECT_, because when this test case is executed in the
316   // child process, the server registration will fail due to the named pipe
317   // being the same.
318   EXPECT_TRUE(server.Start());
319   EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
320   ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
321   ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
322   ASSERT_TRUE(DoesPathExist(full_dump_file.c_str()));
323 
324   // Verify the dump for infos.
325   DumpAnalysis mini(dump_file);
326   DumpAnalysis full(full_dump_file);
327 
328   // The dump should have all of these streams.
329   EXPECT_TRUE(mini.HasStream(ThreadListStream));
330   EXPECT_TRUE(full.HasStream(ThreadListStream));
331   EXPECT_TRUE(mini.HasStream(ModuleListStream));
332   EXPECT_TRUE(full.HasStream(ModuleListStream));
333   EXPECT_TRUE(mini.HasStream(ExceptionStream));
334   EXPECT_TRUE(full.HasStream(ExceptionStream));
335   EXPECT_TRUE(mini.HasStream(SystemInfoStream));
336   EXPECT_TRUE(full.HasStream(SystemInfoStream));
337   EXPECT_TRUE(mini.HasStream(MiscInfoStream));
338   EXPECT_TRUE(full.HasStream(MiscInfoStream));
339   EXPECT_TRUE(mini.HasStream(HandleDataStream));
340   EXPECT_TRUE(full.HasStream(HandleDataStream));
341 
342   // We expect PEB and TEBs in this dump.
343   EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
344   EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
345 
346   // Minidump should have a memory listing, but no 64-bit memory.
347   EXPECT_TRUE(mini.HasStream(MemoryListStream));
348   EXPECT_FALSE(mini.HasStream(Memory64ListStream));
349 
350   EXPECT_FALSE(full.HasStream(MemoryListStream));
351   EXPECT_TRUE(full.HasStream(Memory64ListStream));
352 
353   // This is the only place we don't use OR because we want both not
354   // to have the streams.
355   EXPECT_FALSE(mini.HasStream(ThreadExListStream));
356   EXPECT_FALSE(full.HasStream(ThreadExListStream));
357   EXPECT_FALSE(mini.HasStream(CommentStreamA));
358   EXPECT_FALSE(full.HasStream(CommentStreamA));
359   EXPECT_FALSE(mini.HasStream(CommentStreamW));
360   EXPECT_FALSE(full.HasStream(CommentStreamW));
361   EXPECT_FALSE(mini.HasStream(FunctionTableStream));
362   EXPECT_FALSE(full.HasStream(FunctionTableStream));
363   EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
364   EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
365   EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
366   EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
367   EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
368   EXPECT_FALSE(full.HasStream(HandleOperationListStream));
369   EXPECT_FALSE(mini.HasStream(TokenStream));
370   EXPECT_FALSE(full.HasStream(TokenStream));
371 }
372 
373 // Test that writing a minidump produces a valid minidump containing
374 // some expected structures.
TEST_F(ExceptionHandlerTest,WriteMinidumpTest)375 TEST_F(ExceptionHandlerTest, WriteMinidumpTest) {
376   ExceptionHandler handler(temp_path_,
377                            NULL,
378                            DumpCallback,
379                            NULL,
380                            ExceptionHandler::HANDLER_ALL);
381 
382   // Disable GTest SEH handler
383   testing::DisableExceptionHandlerInScope disable_exception_handler;
384 
385   ASSERT_TRUE(handler.WriteMinidump());
386   ASSERT_FALSE(dump_file.empty());
387 
388   string minidump_filename;
389   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
390                                                 &minidump_filename));
391 
392   // Read the minidump and verify some info.
393   Minidump minidump(minidump_filename);
394   ASSERT_TRUE(minidump.Read());
395   // TODO(ted): more comprehensive tests...
396 }
397 
398 // Test that an additional memory region can be included in the minidump.
TEST_F(ExceptionHandlerTest,AdditionalMemory)399 TEST_F(ExceptionHandlerTest, AdditionalMemory) {
400   SYSTEM_INFO si;
401   GetSystemInfo(&si);
402   const uint32_t kMemorySize = si.dwPageSize;
403 
404   // Get some heap memory.
405   uint8_t* memory = new uint8_t[kMemorySize];
406   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
407   ASSERT_TRUE(memory);
408 
409   // Stick some data into the memory so the contents can be verified.
410   for (uint32_t i = 0; i < kMemorySize; ++i) {
411     memory[i] = i % 255;
412   }
413 
414   ExceptionHandler handler(temp_path_,
415                            NULL,
416                            DumpCallback,
417                            NULL,
418                            ExceptionHandler::HANDLER_ALL);
419 
420   // Disable GTest SEH handler
421   testing::DisableExceptionHandlerInScope disable_exception_handler;
422 
423   // Add the memory region to the list of memory to be included.
424   handler.RegisterAppMemory(memory, kMemorySize);
425   ASSERT_TRUE(handler.WriteMinidump());
426   ASSERT_FALSE(dump_file.empty());
427 
428   string minidump_filename;
429   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
430                                                 &minidump_filename));
431 
432   // Read the minidump. Ensure that the memory region is present
433   Minidump minidump(minidump_filename);
434   ASSERT_TRUE(minidump.Read());
435 
436   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
437   ASSERT_TRUE(dump_memory_list);
438   const MinidumpMemoryRegion* region =
439     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
440   ASSERT_TRUE(region);
441 
442   EXPECT_EQ(kMemoryAddress, region->GetBase());
443   EXPECT_EQ(kMemorySize, region->GetSize());
444 
445   // Verify memory contents.
446   EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
447 
448   delete[] memory;
449 }
450 
451 // Test that a memory region that was previously registered
452 // can be unregistered.
TEST_F(ExceptionHandlerTest,AdditionalMemoryRemove)453 TEST_F(ExceptionHandlerTest, AdditionalMemoryRemove) {
454   SYSTEM_INFO si;
455   GetSystemInfo(&si);
456   const uint32_t kMemorySize = si.dwPageSize;
457 
458   // Get some heap memory.
459   uint8_t* memory = new uint8_t[kMemorySize];
460   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
461   ASSERT_TRUE(memory);
462 
463   // Stick some data into the memory so the contents can be verified.
464   for (uint32_t i = 0; i < kMemorySize; ++i) {
465     memory[i] = i % 255;
466   }
467 
468   ExceptionHandler handler(temp_path_,
469                            NULL,
470                            DumpCallback,
471                            NULL,
472                            ExceptionHandler::HANDLER_ALL);
473 
474   // Disable GTest SEH handler
475   testing::DisableExceptionHandlerInScope disable_exception_handler;
476 
477   // Add the memory region to the list of memory to be included.
478   handler.RegisterAppMemory(memory, kMemorySize);
479 
480   // ...and then remove it
481   handler.UnregisterAppMemory(memory);
482 
483   ASSERT_TRUE(handler.WriteMinidump());
484   ASSERT_FALSE(dump_file.empty());
485 
486   string minidump_filename;
487   ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file,
488                                                 &minidump_filename));
489 
490   // Read the minidump. Ensure that the memory region is not present.
491   Minidump minidump(minidump_filename);
492   ASSERT_TRUE(minidump.Read());
493 
494   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
495   ASSERT_TRUE(dump_memory_list);
496   const MinidumpMemoryRegion* region =
497     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
498   EXPECT_FALSE(region);
499 
500   delete[] memory;
501 }
502 
503 }  // namespace
504