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