1 // Copyright 2010 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 <windows.h>
34 #include <objbase.h>
35 #include <dbghelp.h>
36
37 #include "breakpad_googletest_includes.h"
38 #include "client/windows/unittests/dump_analysis.h" // NOLINT
39
~DumpAnalysis()40 DumpAnalysis::~DumpAnalysis() {
41 if (dump_file_view_ != NULL) {
42 EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_));
43 ::CloseHandle(dump_file_mapping_);
44 dump_file_mapping_ = NULL;
45 }
46
47 if (dump_file_handle_ != NULL) {
48 ::CloseHandle(dump_file_handle_);
49 dump_file_handle_ = NULL;
50 }
51 }
52
EnsureDumpMapped()53 void DumpAnalysis::EnsureDumpMapped() {
54 if (dump_file_view_ == NULL) {
55 dump_file_handle_ = ::CreateFile(dump_file_.c_str(),
56 GENERIC_READ,
57 0,
58 NULL,
59 OPEN_EXISTING,
60 0,
61 NULL);
62 ASSERT_TRUE(dump_file_handle_ != NULL);
63 ASSERT_TRUE(dump_file_mapping_ == NULL);
64
65 dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_,
66 NULL,
67 PAGE_READONLY,
68 0,
69 0,
70 NULL);
71 ASSERT_TRUE(dump_file_mapping_ != NULL);
72
73 dump_file_view_ = ::MapViewOfFile(dump_file_mapping_,
74 FILE_MAP_READ,
75 0,
76 0,
77 0);
78 ASSERT_TRUE(dump_file_view_ != NULL);
79 }
80 }
81
HasTebs() const82 bool DumpAnalysis::HasTebs() const {
83 MINIDUMP_THREAD_LIST* thread_list = NULL;
84 size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
85
86 if (thread_list_size > 0 && thread_list != NULL) {
87 for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) {
88 if (!HasMemory(thread_list->Threads[i].Teb))
89 return false;
90 }
91
92 return true;
93 }
94
95 // No thread list, no TEB info.
96 return false;
97 }
98
HasPeb() const99 bool DumpAnalysis::HasPeb() const {
100 MINIDUMP_THREAD_LIST* thread_list = NULL;
101 size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
102
103 if (thread_list_size > 0 && thread_list != NULL &&
104 thread_list->NumberOfThreads > 0) {
105 FakeTEB* teb = NULL;
106 if (!HasMemory(thread_list->Threads[0].Teb, &teb))
107 return false;
108
109 return HasMemory(teb->peb);
110 }
111
112 return false;
113 }
114
HasStream(ULONG stream_number) const115 bool DumpAnalysis::HasStream(ULONG stream_number) const {
116 void* stream = NULL;
117 size_t stream_size = GetStreamImpl(stream_number, &stream);
118 return stream_size > 0 && stream != NULL;
119 }
120
GetStreamImpl(ULONG stream_number,void ** stream) const121 size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const {
122 MINIDUMP_DIRECTORY* directory = NULL;
123 ULONG memory_list_size = 0;
124 BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_,
125 stream_number,
126 &directory,
127 stream,
128 &memory_list_size);
129
130 return ret ? memory_list_size : 0;
131 }
132
HasMemoryImpl(const void * addr_in,size_t structuresize,void ** structure) const133 bool DumpAnalysis::HasMemoryImpl(const void* addr_in, size_t structuresize,
134 void** structure) const {
135 uintptr_t address = reinterpret_cast<uintptr_t>(addr_in);
136 MINIDUMP_MEMORY_LIST* memory_list = NULL;
137 size_t memory_list_size = GetStream(MemoryListStream, &memory_list);
138 if (memory_list_size > 0 && memory_list != NULL) {
139 for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) {
140 MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i];
141 const uintptr_t range_start =
142 static_cast<uintptr_t>(descr.StartOfMemoryRange);
143 uintptr_t range_end = range_start + descr.Memory.DataSize;
144
145 if (address >= range_start &&
146 address + structuresize < range_end) {
147 // The start address falls in the range, and the end address is
148 // in bounds, return a pointer to the structure if requested.
149 if (structure != NULL)
150 *structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva);
151
152 return true;
153 }
154 }
155 }
156
157 // We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this
158 // is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the
159 // end of the dump file.
160 MINIDUMP_MEMORY64_LIST* memory64_list = NULL;
161 memory_list_size = GetStream(Memory64ListStream, &memory64_list);
162 if (memory_list_size > 0 && memory64_list != NULL) {
163 // Keep track of where the current descriptor maps to.
164 RVA64 curr_rva = memory64_list->BaseRva;
165 for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) {
166 MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i];
167 uintptr_t range_start =
168 static_cast<uintptr_t>(descr.StartOfMemoryRange);
169 uintptr_t range_end = range_start + static_cast<size_t>(descr.DataSize);
170
171 if (address >= range_start &&
172 address + structuresize < range_end) {
173 // The start address falls in the range, and the end address is
174 // in bounds, return a pointer to the structure if requested.
175 if (structure != NULL)
176 *structure = RVA_TO_ADDR(dump_file_view_, curr_rva);
177
178 return true;
179 }
180
181 // Advance the current RVA.
182 curr_rva += descr.DataSize;
183 }
184 }
185
186 return false;
187 }
188