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
47namespace testing {
48
49DisableExceptionHandlerInScope::DisableExceptionHandlerInScope() {
50  catch_exceptions_ = GTEST_FLAG(catch_exceptions);
51  GTEST_FLAG(catch_exceptions) = false;
52}
53
54DisableExceptionHandlerInScope::~DisableExceptionHandlerInScope() {
55  GTEST_FLAG(catch_exceptions) = catch_exceptions_;
56}
57
58}  // namespace testing
59
60namespace {
61
62using std::wstring;
63using namespace google_breakpad;
64
65const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
66const char kSuccessIndicator[] = "success";
67const char kFailureIndicator[] = "failure";
68
69const 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
74class 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
109std::wstring ExceptionHandlerTest::dump_file;
110std::wstring ExceptionHandlerTest::full_dump_file;
111
112void 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
133void 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
144BOOL 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
153void 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
163bool 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
176void 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
194struct PureVirtualCallBase {
195  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  }
200  virtual ~PureVirtualCallBase() {}
201  virtual void PureFunction() const = 0;
202};
203struct PureVirtualCall : public PureVirtualCallBase {
204  PureVirtualCall() { PureFunction(); }
205  virtual void PureFunction() const {}
206};
207
208void 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.
231TEST_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.
302TEST_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.
373TEST_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.
397TEST_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.
451TEST_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