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 #if defined(_MSC_VER) && !defined(_LIBCPP_STD_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 write_dump_(NULL),
263 rpcrt4_module_(NULL),
264 create_uuid_(NULL),
265 process_handle_(process_handle),
266 process_id_(process_id),
267 thread_id_(thread_id),
268 requesting_thread_id_(requesting_thread_id),
269 exception_pointers_(exception_pointers),
270 assert_info_(assert_info),
271 dump_type_(dump_type),
272 is_client_pointers_(is_client_pointers),
273 dump_path_(dump_path),
274 uuid_generated_(false),
275 dump_file_(INVALID_HANDLE_VALUE),
276 full_dump_file_(INVALID_HANDLE_VALUE),
277 dump_file_is_internal_(false),
278 full_dump_file_is_internal_(false),
279 additional_streams_(NULL),
280 callback_info_(NULL) {
281 uuid_ = {0};
282 InitializeCriticalSection(&module_load_sync_);
283 InitializeCriticalSection(&get_proc_address_sync_);
284 }
285
~MinidumpGenerator()286 MinidumpGenerator::~MinidumpGenerator() {
287 if (dump_file_is_internal_ && dump_file_ != INVALID_HANDLE_VALUE) {
288 CloseHandle(dump_file_);
289 }
290
291 if (full_dump_file_is_internal_ && full_dump_file_ != INVALID_HANDLE_VALUE) {
292 CloseHandle(full_dump_file_);
293 }
294
295 if (dbghelp_module_) {
296 FreeLibrary(dbghelp_module_);
297 }
298
299 if (rpcrt4_module_) {
300 FreeLibrary(rpcrt4_module_);
301 }
302
303 DeleteCriticalSection(&get_proc_address_sync_);
304 DeleteCriticalSection(&module_load_sync_);
305 }
306
WriteMinidump()307 bool MinidumpGenerator::WriteMinidump() {
308 bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0;
309 if (dump_file_ == INVALID_HANDLE_VALUE ||
310 (full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) {
311 return false;
312 }
313
314 MiniDumpWriteDumpType write_dump = GetWriteDump();
315 if (!write_dump) {
316 return false;
317 }
318
319 MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL;
320 MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
321
322 // Setup the exception information object only if it's a dump
323 // due to an exception.
324 if (exception_pointers_) {
325 dump_exception_pointers = &dump_exception_info;
326 dump_exception_info.ThreadId = thread_id_;
327 dump_exception_info.ExceptionPointers = exception_pointers_;
328 dump_exception_info.ClientPointers = is_client_pointers_;
329 }
330
331 // Add an MDRawBreakpadInfo stream to the minidump, to provide additional
332 // information about the exception handler to the Breakpad processor.
333 // The information will help the processor determine which threads are
334 // relevant. The Breakpad processor does not require this information but
335 // can function better with Breakpad-generated dumps when it is present.
336 // The native debugger is not harmed by the presence of this information.
337 MDRawBreakpadInfo breakpad_info = {0};
338 if (!is_client_pointers_) {
339 // Set the dump thread id and requesting thread id only in case of
340 // in-process dump generation.
341 breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
342 MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
343 breakpad_info.dump_thread_id = thread_id_;
344 breakpad_info.requesting_thread_id = requesting_thread_id_;
345 }
346
347 int additional_streams_count = additional_streams_ ?
348 additional_streams_->UserStreamCount : 0;
349 scoped_array<MINIDUMP_USER_STREAM> user_stream_array(
350 new MINIDUMP_USER_STREAM[3 + additional_streams_count]);
351 user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
352 user_stream_array[0].BufferSize = sizeof(breakpad_info);
353 user_stream_array[0].Buffer = &breakpad_info;
354
355 MINIDUMP_USER_STREAM_INFORMATION user_streams;
356 user_streams.UserStreamCount = 1;
357 user_streams.UserStreamArray = user_stream_array.get();
358
359 MDRawAssertionInfo* actual_assert_info = assert_info_;
360 MDRawAssertionInfo client_assert_info = {{0}};
361
362 if (assert_info_) {
363 // If the assertion info object lives in the client process,
364 // read the memory of the client process.
365 if (is_client_pointers_) {
366 SIZE_T bytes_read = 0;
367 if (!ReadProcessMemory(process_handle_,
368 assert_info_,
369 &client_assert_info,
370 sizeof(client_assert_info),
371 &bytes_read)) {
372 if (dump_file_is_internal_)
373 CloseHandle(dump_file_);
374 if (full_dump_file_is_internal_ &&
375 full_dump_file_ != INVALID_HANDLE_VALUE)
376 CloseHandle(full_dump_file_);
377 return false;
378 }
379
380 if (bytes_read != sizeof(client_assert_info)) {
381 if (dump_file_is_internal_)
382 CloseHandle(dump_file_);
383 if (full_dump_file_is_internal_ &&
384 full_dump_file_ != INVALID_HANDLE_VALUE)
385 CloseHandle(full_dump_file_);
386 return false;
387 }
388
389 actual_assert_info = &client_assert_info;
390 }
391
392 user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
393 user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
394 user_stream_array[1].Buffer = actual_assert_info;
395 ++user_streams.UserStreamCount;
396 }
397
398 if (additional_streams_) {
399 for (size_t i = 0;
400 i < additional_streams_->UserStreamCount;
401 i++, user_streams.UserStreamCount++) {
402 user_stream_array[user_streams.UserStreamCount].Type =
403 additional_streams_->UserStreamArray[i].Type;
404 user_stream_array[user_streams.UserStreamCount].BufferSize =
405 additional_streams_->UserStreamArray[i].BufferSize;
406 user_stream_array[user_streams.UserStreamCount].Buffer =
407 additional_streams_->UserStreamArray[i].Buffer;
408 }
409 }
410
411 // If the process is terminated by STATUS_INVALID_HANDLE exception store
412 // the trace of operations for the offending handle value. Do nothing special
413 // if the client already requested the handle trace to be stored in the dump.
414 HandleTraceData handle_trace_data;
415 if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) {
416 if (!handle_trace_data.CollectHandleData(process_handle_,
417 exception_pointers_)) {
418 if (dump_file_is_internal_)
419 CloseHandle(dump_file_);
420 if (full_dump_file_is_internal_ &&
421 full_dump_file_ != INVALID_HANDLE_VALUE)
422 CloseHandle(full_dump_file_);
423 return false;
424 }
425 }
426
427 bool result_full_memory = true;
428 if (full_memory_dump) {
429 result_full_memory = write_dump(
430 process_handle_,
431 process_id_,
432 full_dump_file_,
433 static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpNormal))
434 | MiniDumpWithHandleData),
435 exception_pointers_ ? &dump_exception_info : NULL,
436 &user_streams,
437 NULL) != FALSE;
438 }
439
440 // Add handle operations trace stream to the minidump if it was collected.
441 if (handle_trace_data.GetUserStream(
442 &user_stream_array[user_streams.UserStreamCount])) {
443 ++user_streams.UserStreamCount;
444 }
445
446 bool result_minidump = write_dump(
447 process_handle_,
448 process_id_,
449 dump_file_,
450 static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpWithFullMemory))
451 | MiniDumpNormal),
452 exception_pointers_ ? &dump_exception_info : NULL,
453 &user_streams,
454 callback_info_) != FALSE;
455
456 return result_minidump && result_full_memory;
457 }
458
GenerateDumpFile(wstring * dump_path)459 bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) {
460 // The dump file was already set by handle or this function was previously
461 // called.
462 if (dump_file_ != INVALID_HANDLE_VALUE) {
463 return false;
464 }
465
466 wstring dump_file_path;
467 if (!GenerateDumpFilePath(&dump_file_path)) {
468 return false;
469 }
470
471 dump_file_ = CreateFile(dump_file_path.c_str(),
472 GENERIC_WRITE,
473 0,
474 NULL,
475 CREATE_NEW,
476 FILE_ATTRIBUTE_NORMAL,
477 NULL);
478 if (dump_file_ == INVALID_HANDLE_VALUE) {
479 return false;
480 }
481
482 dump_file_is_internal_ = true;
483 *dump_path = dump_file_path;
484 return true;
485 }
486
GenerateFullDumpFile(wstring * full_dump_path)487 bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) {
488 // A full minidump was not requested.
489 if ((dump_type_ & MiniDumpWithFullMemory) == 0) {
490 return false;
491 }
492
493 // The dump file was already set by handle or this function was previously
494 // called.
495 if (full_dump_file_ != INVALID_HANDLE_VALUE) {
496 return false;
497 }
498
499 wstring full_dump_file_path;
500 if (!GenerateDumpFilePath(&full_dump_file_path)) {
501 return false;
502 }
503 full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp
504 full_dump_file_path.append(TEXT("-full.dmp"));
505
506 full_dump_file_ = CreateFile(full_dump_file_path.c_str(),
507 GENERIC_WRITE,
508 0,
509 NULL,
510 CREATE_NEW,
511 FILE_ATTRIBUTE_NORMAL,
512 NULL);
513 if (full_dump_file_ == INVALID_HANDLE_VALUE) {
514 return false;
515 }
516
517 full_dump_file_is_internal_ = true;
518 *full_dump_path = full_dump_file_path;
519 return true;
520 }
521
GetDbghelpModule()522 HMODULE MinidumpGenerator::GetDbghelpModule() {
523 AutoCriticalSection lock(&module_load_sync_);
524 if (!dbghelp_module_) {
525 dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll"));
526 }
527
528 return dbghelp_module_;
529 }
530
GetWriteDump()531 MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() {
532 AutoCriticalSection lock(&get_proc_address_sync_);
533 if (!write_dump_) {
534 HMODULE module = GetDbghelpModule();
535 if (module) {
536 FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump");
537 write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc);
538 }
539 }
540
541 return write_dump_;
542 }
543
GetRpcrt4Module()544 HMODULE MinidumpGenerator::GetRpcrt4Module() {
545 AutoCriticalSection lock(&module_load_sync_);
546 if (!rpcrt4_module_) {
547 rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll"));
548 }
549
550 return rpcrt4_module_;
551 }
552
GetCreateUuid()553 MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
554 AutoCriticalSection lock(&module_load_sync_);
555 if (!create_uuid_) {
556 HMODULE module = GetRpcrt4Module();
557 if (module) {
558 FARPROC proc = GetProcAddress(module, "UuidCreate");
559 create_uuid_ = reinterpret_cast<UuidCreateType>(proc);
560 }
561 }
562
563 return create_uuid_;
564 }
565
GenerateDumpFilePath(wstring * file_path)566 bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
567 if (!uuid_generated_) {
568 UuidCreateType create_uuid = GetCreateUuid();
569 if (!create_uuid) {
570 return false;
571 }
572
573 create_uuid(&uuid_);
574 uuid_generated_ = true;
575 }
576
577 wstring id_str = GUIDString::GUIDToWString(&uuid_);
578
579 *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
580 return true;
581 }
582
583 } // namespace google_breakpad
584