xref: /aosp_15_r20/external/google-breakpad/src/client/windows/unittests/exception_handler_test.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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