xref: /aosp_15_r20/external/google-breakpad/src/client/windows/unittests/dump_analysis.cc (revision 9712c20fc9bbfbac4935993a2ca0b3958c5adad2)
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