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