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
44using std::wstring;
45
46namespace {
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.
51class 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
103HandleTraceData::HandleTraceData()
104    : verifier_module_(NULL),
105      enumerate_resource_(NULL),
106      handle_(NULL) {
107}
108
109HandleTraceData::~HandleTraceData() {
110  if (verifier_module_) {
111    FreeLibrary(verifier_module_);
112  }
113}
114
115bool 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
191bool 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
202bool 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
226ULONG 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
249namespace google_breakpad {
250
251MinidumpGenerator::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
284MinidumpGenerator::~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
305bool 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
457bool 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
485bool 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
520HMODULE 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
529MinidumpGenerator::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
542HMODULE 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
551MinidumpGenerator::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
564bool 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