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