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