xref: /aosp_15_r20/external/google-breakpad/src/client/windows/crash_generation/minidump_generator.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
1 // Copyright 2008 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/crash_generation/minidump_generator.h"
34 
35 #include <assert.h>
36 #include <avrfsdk.h>
37 
38 #include <algorithm>
39 #include <iterator>
40 #include <list>
41 #include <vector>
42 
43 #include "client/windows/common/auto_critical_section.h"
44 #include "common/scoped_ptr.h"
45 #include "common/windows/guid_string.h"
46 
47 using std::wstring;
48 
49 namespace {
50 
51 // A helper class used to collect handle operations data. Unlike
52 // |MiniDumpWithHandleData| it records the operations for a single handle value
53 // only, making it possible to include this information to a minidump.
54 class HandleTraceData {
55  public:
56   HandleTraceData();
57   ~HandleTraceData();
58 
59   // Collects the handle operations data and formats a user stream to be added
60   // to the minidump.
61   bool CollectHandleData(HANDLE process_handle,
62                          EXCEPTION_POINTERS* exception_pointers);
63 
64   // Fills the user dump entry with a pointer to the collected handle operations
65   // data. Returns |true| if the entry was initialized successfully, or |false|
66   // if no trace data is available.
67   bool GetUserStream(MINIDUMP_USER_STREAM* user_stream);
68 
69  private:
70   // Reads the exception code from the client process's address space.
71   // This routine assumes that the client process's pointer width matches ours.
72   static bool ReadExceptionCode(HANDLE process_handle,
73                                 EXCEPTION_POINTERS* exception_pointers,
74                                 DWORD* exception_code);
75 
76   // Stores handle operations retrieved by VerifierEnumerateResource().
77   static ULONG CALLBACK RecordHandleOperations(void* resource_description,
78                                                void* enumeration_context,
79                                                ULONG* enumeration_level);
80 
81   // Function pointer type for VerifierEnumerateResource, which is looked up
82   // dynamically.
83   typedef BOOL (WINAPI* VerifierEnumerateResourceType)(
84       HANDLE Process,
85       ULONG Flags,
86       ULONG ResourceType,
87       AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback,
88       PVOID EnumerationContext);
89 
90   // Handle to dynamically loaded verifier.dll.
91   HMODULE verifier_module_;
92 
93   // Pointer to the VerifierEnumerateResource function.
94   VerifierEnumerateResourceType enumerate_resource_;
95 
96   // Handle value to look for.
97   ULONG64 handle_;
98 
99   // List of handle operations for |handle_|.
100   std::list<AVRF_HANDLE_OPERATION> operations_;
101 
102   // Minidump stream data.
103   std::vector<char> stream_;
104 };
105 
HandleTraceData()106 HandleTraceData::HandleTraceData()
107     : verifier_module_(NULL),
108       enumerate_resource_(NULL),
109       handle_(NULL) {
110 }
111 
~HandleTraceData()112 HandleTraceData::~HandleTraceData() {
113   if (verifier_module_) {
114     FreeLibrary(verifier_module_);
115   }
116 }
117 
CollectHandleData(HANDLE process_handle,EXCEPTION_POINTERS * exception_pointers)118 bool HandleTraceData::CollectHandleData(
119     HANDLE process_handle,
120     EXCEPTION_POINTERS* exception_pointers) {
121   DWORD exception_code;
122   if (!ReadExceptionCode(process_handle, exception_pointers, &exception_code)) {
123     return false;
124   }
125 
126   // Verify whether the execption is STATUS_INVALID_HANDLE. Do not record any
127   // handle information if it is a different exception to keep the minidump
128   // small.
129   if (exception_code != STATUS_INVALID_HANDLE) {
130     return true;
131   }
132 
133   // Load verifier!VerifierEnumerateResource() dynamically.
134   verifier_module_ = LoadLibrary(TEXT("verifier.dll"));
135   if (!verifier_module_) {
136     return false;
137   }
138 
139   enumerate_resource_ = reinterpret_cast<VerifierEnumerateResourceType>(
140       GetProcAddress(verifier_module_, "VerifierEnumerateResource"));
141   if (!enumerate_resource_) {
142     return false;
143   }
144 
145   // STATUS_INVALID_HANDLE does not provide the offending handle value in
146   // the exception parameters so we have to guess. At the moment we scan
147   // the handle operations trace looking for the last invalid handle operation
148   // and record only the operations for that handle value.
149   if (enumerate_resource_(process_handle,
150                           0,
151                           AvrfResourceHandleTrace,
152                           &RecordHandleOperations,
153                           this) != ERROR_SUCCESS) {
154     // The handle tracing must have not been enabled.
155     return true;
156   }
157 
158   // Now that |handle_| is initialized, purge all irrelevant operations.
159   std::list<AVRF_HANDLE_OPERATION>::iterator i = operations_.begin();
160   std::list<AVRF_HANDLE_OPERATION>::iterator i_end = operations_.end();
161   while (i != i_end) {
162     if (i->Handle == handle_) {
163       ++i;
164     } else {
165       i = operations_.erase(i);
166     }
167   }
168 
169   // Convert the list of recorded operations to a minidump stream.
170   stream_.resize(sizeof(MINIDUMP_HANDLE_OPERATION_LIST) +
171       sizeof(AVRF_HANDLE_OPERATION) * operations_.size());
172 
173   MINIDUMP_HANDLE_OPERATION_LIST* stream_data =
174       reinterpret_cast<MINIDUMP_HANDLE_OPERATION_LIST*>(
175           &stream_.front());
176   stream_data->SizeOfHeader = sizeof(MINIDUMP_HANDLE_OPERATION_LIST);
177   stream_data->SizeOfEntry = sizeof(AVRF_HANDLE_OPERATION);
178   stream_data->NumberOfEntries = static_cast<ULONG32>(operations_.size());
179   stream_data->Reserved = 0;
180   std::copy(operations_.begin(),
181             operations_.end(),
182 #if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER)
183             stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>(
184                 reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1),
185                 operations_.size())
186 #else
187             reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1)
188 #endif
189             );
190 
191   return true;
192 }
193 
GetUserStream(MINIDUMP_USER_STREAM * user_stream)194 bool HandleTraceData::GetUserStream(MINIDUMP_USER_STREAM* user_stream) {
195   if (stream_.empty()) {
196     return false;
197   } else {
198     user_stream->Type = HandleOperationListStream;
199     user_stream->BufferSize = static_cast<ULONG>(stream_.size());
200     user_stream->Buffer = &stream_.front();
201     return true;
202   }
203 }
204 
ReadExceptionCode(HANDLE process_handle,EXCEPTION_POINTERS * exception_pointers,DWORD * exception_code)205 bool HandleTraceData::ReadExceptionCode(
206     HANDLE process_handle,
207     EXCEPTION_POINTERS* exception_pointers,
208     DWORD* exception_code) {
209   EXCEPTION_POINTERS pointers;
210   if (!ReadProcessMemory(process_handle,
211                          exception_pointers,
212                          &pointers,
213                          sizeof(pointers),
214                          NULL)) {
215     return false;
216   }
217 
218   if (!ReadProcessMemory(process_handle,
219                          pointers.ExceptionRecord,
220                          exception_code,
221                          sizeof(*exception_code),
222                          NULL)) {
223     return false;
224   }
225 
226   return true;
227 }
228 
RecordHandleOperations(void * resource_description,void * enumeration_context,ULONG * enumeration_level)229 ULONG CALLBACK HandleTraceData::RecordHandleOperations(
230     void* resource_description,
231     void* enumeration_context,
232     ULONG* enumeration_level) {
233   AVRF_HANDLE_OPERATION* description =
234       reinterpret_cast<AVRF_HANDLE_OPERATION*>(resource_description);
235   HandleTraceData* self =
236       reinterpret_cast<HandleTraceData*>(enumeration_context);
237 
238   // Remember the last invalid handle operation.
239   if (description->OperationType == OperationDbBADREF) {
240     self->handle_ = description->Handle;
241   }
242 
243   // Record all handle operations.
244   self->operations_.push_back(*description);
245 
246   *enumeration_level = HeapEnumerationEverything;
247   return ERROR_SUCCESS;
248 }
249 
250 }  // namespace
251 
252 namespace google_breakpad {
253 
MinidumpGenerator(const std::wstring & dump_path,const HANDLE process_handle,const DWORD process_id,const DWORD thread_id,const DWORD requesting_thread_id,EXCEPTION_POINTERS * exception_pointers,MDRawAssertionInfo * assert_info,const MINIDUMP_TYPE dump_type,const bool is_client_pointers)254 MinidumpGenerator::MinidumpGenerator(
255     const std::wstring& dump_path,
256     const HANDLE process_handle,
257     const DWORD process_id,
258     const DWORD thread_id,
259     const DWORD requesting_thread_id,
260     EXCEPTION_POINTERS* exception_pointers,
261     MDRawAssertionInfo* assert_info,
262     const MINIDUMP_TYPE dump_type,
263     const bool is_client_pointers)
264     : dbghelp_module_(NULL),
265       write_dump_(NULL),
266       rpcrt4_module_(NULL),
267       create_uuid_(NULL),
268       process_handle_(process_handle),
269       process_id_(process_id),
270       thread_id_(thread_id),
271       requesting_thread_id_(requesting_thread_id),
272       exception_pointers_(exception_pointers),
273       assert_info_(assert_info),
274       dump_type_(dump_type),
275       is_client_pointers_(is_client_pointers),
276       dump_path_(dump_path),
277       uuid_generated_(false),
278       dump_file_(INVALID_HANDLE_VALUE),
279       full_dump_file_(INVALID_HANDLE_VALUE),
280       dump_file_is_internal_(false),
281       full_dump_file_is_internal_(false),
282       additional_streams_(NULL),
283       callback_info_(NULL) {
284   uuid_ = {0};
285   InitializeCriticalSection(&module_load_sync_);
286   InitializeCriticalSection(&get_proc_address_sync_);
287 }
288 
~MinidumpGenerator()289 MinidumpGenerator::~MinidumpGenerator() {
290   if (dump_file_is_internal_ && dump_file_ != INVALID_HANDLE_VALUE) {
291     CloseHandle(dump_file_);
292   }
293 
294   if (full_dump_file_is_internal_ && full_dump_file_ != INVALID_HANDLE_VALUE) {
295     CloseHandle(full_dump_file_);
296   }
297 
298   if (dbghelp_module_) {
299     FreeLibrary(dbghelp_module_);
300   }
301 
302   if (rpcrt4_module_) {
303     FreeLibrary(rpcrt4_module_);
304   }
305 
306   DeleteCriticalSection(&get_proc_address_sync_);
307   DeleteCriticalSection(&module_load_sync_);
308 }
309 
WriteMinidump()310 bool MinidumpGenerator::WriteMinidump() {
311   bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0;
312   if (dump_file_ == INVALID_HANDLE_VALUE ||
313       (full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) {
314     return false;
315   }
316 
317   MiniDumpWriteDumpType write_dump = GetWriteDump();
318   if (!write_dump) {
319     return false;
320   }
321 
322   MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL;
323   MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
324 
325   // Setup the exception information object only if it's a dump
326   // due to an exception.
327   if (exception_pointers_) {
328     dump_exception_pointers = &dump_exception_info;
329     dump_exception_info.ThreadId = thread_id_;
330     dump_exception_info.ExceptionPointers = exception_pointers_;
331     dump_exception_info.ClientPointers = is_client_pointers_;
332   }
333 
334   // Add an MDRawBreakpadInfo stream to the minidump, to provide additional
335   // information about the exception handler to the Breakpad processor.
336   // The information will help the processor determine which threads are
337   // relevant. The Breakpad processor does not require this information but
338   // can function better with Breakpad-generated dumps when it is present.
339   // The native debugger is not harmed by the presence of this information.
340   MDRawBreakpadInfo breakpad_info = {0};
341   if (!is_client_pointers_) {
342     // Set the dump thread id and requesting thread id only in case of
343     // in-process dump generation.
344     breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
345                              MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
346     breakpad_info.dump_thread_id = thread_id_;
347     breakpad_info.requesting_thread_id = requesting_thread_id_;
348   }
349 
350   int additional_streams_count = additional_streams_ ?
351       additional_streams_->UserStreamCount : 0;
352   scoped_array<MINIDUMP_USER_STREAM> user_stream_array(
353       new MINIDUMP_USER_STREAM[3 + additional_streams_count]);
354   user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
355   user_stream_array[0].BufferSize = sizeof(breakpad_info);
356   user_stream_array[0].Buffer = &breakpad_info;
357 
358   MINIDUMP_USER_STREAM_INFORMATION user_streams;
359   user_streams.UserStreamCount = 1;
360   user_streams.UserStreamArray = user_stream_array.get();
361 
362   MDRawAssertionInfo* actual_assert_info = assert_info_;
363   MDRawAssertionInfo client_assert_info = {{0}};
364 
365   if (assert_info_) {
366     // If the assertion info object lives in the client process,
367     // read the memory of the client process.
368     if (is_client_pointers_) {
369       SIZE_T bytes_read = 0;
370       if (!ReadProcessMemory(process_handle_,
371                              assert_info_,
372                              &client_assert_info,
373                              sizeof(client_assert_info),
374                              &bytes_read)) {
375         if (dump_file_is_internal_)
376           CloseHandle(dump_file_);
377         if (full_dump_file_is_internal_ &&
378             full_dump_file_ != INVALID_HANDLE_VALUE)
379           CloseHandle(full_dump_file_);
380         return false;
381       }
382 
383       if (bytes_read != sizeof(client_assert_info)) {
384         if (dump_file_is_internal_)
385           CloseHandle(dump_file_);
386         if (full_dump_file_is_internal_ &&
387             full_dump_file_ != INVALID_HANDLE_VALUE)
388           CloseHandle(full_dump_file_);
389         return false;
390       }
391 
392       actual_assert_info  = &client_assert_info;
393     }
394 
395     user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
396     user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
397     user_stream_array[1].Buffer = actual_assert_info;
398     ++user_streams.UserStreamCount;
399   }
400 
401   if (additional_streams_) {
402     for (size_t i = 0;
403          i < additional_streams_->UserStreamCount;
404          i++, user_streams.UserStreamCount++) {
405       user_stream_array[user_streams.UserStreamCount].Type =
406           additional_streams_->UserStreamArray[i].Type;
407       user_stream_array[user_streams.UserStreamCount].BufferSize =
408           additional_streams_->UserStreamArray[i].BufferSize;
409       user_stream_array[user_streams.UserStreamCount].Buffer =
410           additional_streams_->UserStreamArray[i].Buffer;
411     }
412   }
413 
414   // If the process is terminated by STATUS_INVALID_HANDLE exception store
415   // the trace of operations for the offending handle value. Do nothing special
416   // if the client already requested the handle trace to be stored in the dump.
417   HandleTraceData handle_trace_data;
418   if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) {
419     if (!handle_trace_data.CollectHandleData(process_handle_,
420                                              exception_pointers_)) {
421       if (dump_file_is_internal_)
422         CloseHandle(dump_file_);
423       if (full_dump_file_is_internal_ &&
424           full_dump_file_ != INVALID_HANDLE_VALUE)
425         CloseHandle(full_dump_file_);
426       return false;
427     }
428   }
429 
430   bool result_full_memory = true;
431   if (full_memory_dump) {
432     result_full_memory = write_dump(
433         process_handle_,
434         process_id_,
435         full_dump_file_,
436         static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpNormal))
437                                     | MiniDumpWithHandleData),
438         dump_exception_pointers,
439         &user_streams,
440         NULL) != FALSE;
441   }
442 
443   // Add handle operations trace stream to the minidump if it was collected.
444   if (handle_trace_data.GetUserStream(
445           &user_stream_array[user_streams.UserStreamCount])) {
446     ++user_streams.UserStreamCount;
447   }
448 
449   bool result_minidump = write_dump(
450       process_handle_,
451       process_id_,
452       dump_file_,
453       static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpWithFullMemory))
454                                   | MiniDumpNormal),
455       dump_exception_pointers,
456       &user_streams,
457       callback_info_) != FALSE;
458 
459   return result_minidump && result_full_memory;
460 }
461 
GenerateDumpFile(wstring * dump_path)462 bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) {
463   // The dump file was already set by handle or this function was previously
464   // called.
465   if (dump_file_ != INVALID_HANDLE_VALUE) {
466     return false;
467   }
468 
469   wstring dump_file_path;
470   if (!GenerateDumpFilePath(&dump_file_path)) {
471     return false;
472   }
473 
474   dump_file_ = CreateFile(dump_file_path.c_str(),
475                           GENERIC_WRITE,
476                           0,
477                           NULL,
478                           CREATE_NEW,
479                           FILE_ATTRIBUTE_NORMAL,
480                           NULL);
481   if (dump_file_ == INVALID_HANDLE_VALUE) {
482     return false;
483   }
484 
485   dump_file_is_internal_ = true;
486   *dump_path = dump_file_path;
487   return true;
488 }
489 
GenerateFullDumpFile(wstring * full_dump_path)490 bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) {
491   // A full minidump was not requested.
492   if ((dump_type_ & MiniDumpWithFullMemory) == 0) {
493     return false;
494   }
495 
496   // The dump file was already set by handle or this function was previously
497   // called.
498   if (full_dump_file_ != INVALID_HANDLE_VALUE) {
499     return false;
500   }
501 
502   wstring full_dump_file_path;
503   if (!GenerateDumpFilePath(&full_dump_file_path)) {
504     return false;
505   }
506   full_dump_file_path.resize(full_dump_file_path.size() - 4);  // strip .dmp
507   full_dump_file_path.append(TEXT("-full.dmp"));
508 
509   full_dump_file_ = CreateFile(full_dump_file_path.c_str(),
510                                GENERIC_WRITE,
511                                0,
512                                NULL,
513                                CREATE_NEW,
514                                FILE_ATTRIBUTE_NORMAL,
515                                NULL);
516   if (full_dump_file_ == INVALID_HANDLE_VALUE) {
517     return false;
518   }
519 
520   full_dump_file_is_internal_ = true;
521   *full_dump_path = full_dump_file_path;
522   return true;
523 }
524 
GetDbghelpModule()525 HMODULE MinidumpGenerator::GetDbghelpModule() {
526   AutoCriticalSection lock(&module_load_sync_);
527   if (!dbghelp_module_) {
528     dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll"));
529   }
530 
531   return dbghelp_module_;
532 }
533 
GetWriteDump()534 MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() {
535   AutoCriticalSection lock(&get_proc_address_sync_);
536   if (!write_dump_) {
537     HMODULE module = GetDbghelpModule();
538     if (module) {
539       FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump");
540       write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc);
541     }
542   }
543 
544   return write_dump_;
545 }
546 
GetRpcrt4Module()547 HMODULE MinidumpGenerator::GetRpcrt4Module() {
548   AutoCriticalSection lock(&module_load_sync_);
549   if (!rpcrt4_module_) {
550     rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll"));
551   }
552 
553   return rpcrt4_module_;
554 }
555 
GetCreateUuid()556 MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
557   AutoCriticalSection lock(&module_load_sync_);
558   if (!create_uuid_) {
559     HMODULE module = GetRpcrt4Module();
560     if (module) {
561       FARPROC proc = GetProcAddress(module, "UuidCreate");
562       create_uuid_ = reinterpret_cast<UuidCreateType>(proc);
563     }
564   }
565 
566   return create_uuid_;
567 }
568 
GenerateDumpFilePath(wstring * file_path)569 bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
570   if (!uuid_generated_) {
571     UuidCreateType create_uuid = GetCreateUuid();
572     if (!create_uuid) {
573       return false;
574     }
575 
576     create_uuid(&uuid_);
577     uuid_generated_ = true;
578   }
579 
580   wstring id_str = GUIDString::GUIDToWString(&uuid_);
581 
582   *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
583   return true;
584 }
585 
586 }  // namespace google_breakpad
587