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