1 // Copyright 2015 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "snapshot/win/exception_snapshot_win.h"
16 
17 #include "base/logging.h"
18 #include "client/crashpad_client.h"
19 #include "snapshot/capture_memory.h"
20 #include "snapshot/memory_snapshot.h"
21 #include "snapshot/memory_snapshot_generic.h"
22 #include "snapshot/win/capture_memory_delegate_win.h"
23 #include "snapshot/win/cpu_context_win.h"
24 #include "snapshot/win/process_reader_win.h"
25 #include "util/win/nt_internals.h"
26 
27 namespace crashpad {
28 namespace internal {
29 
30 namespace {
31 
32 #if defined(ARCH_CPU_X86_FAMILY)
33 #if defined(ARCH_CPU_32_BITS)
34 using Context32 = CONTEXT;
35 #elif defined(ARCH_CPU_64_BITS)
36 using Context32 = WOW64_CONTEXT;
37 #endif
38 
NativeContextToCPUContext32(const Context32 & context_record,CPUContext * context,CPUContextUnion * context_union)39 void NativeContextToCPUContext32(const Context32& context_record,
40                                  CPUContext* context,
41                                  CPUContextUnion* context_union) {
42   context->architecture = kCPUArchitectureX86;
43   context->x86 = &context_union->x86;
44   InitializeX86Context(context_record, context->x86);
45 }
46 #endif  // ARCH_CPU_X86_FAMILY
47 
48 #if defined(ARCH_CPU_64_BITS)
NativeContextToCPUContext64(const CONTEXT & context_record,CPUContext * context,CPUContextUnion * context_union)49 void NativeContextToCPUContext64(const CONTEXT& context_record,
50                                  CPUContext* context,
51                                  CPUContextUnion* context_union) {
52 #if defined(ARCH_CPU_X86_64)
53   context->architecture = kCPUArchitectureX86_64;
54   context->x86_64 = &context_union->x86_64;
55   InitializeX64Context(context_record, context->x86_64);
56 #elif defined(ARCH_CPU_ARM64)
57   context->architecture = kCPUArchitectureARM64;
58   context->arm64 = &context_union->arm64;
59   InitializeARM64Context(context_record, context->arm64);
60 #else
61 #error Unsupported Windows 64-bit Arch
62 #endif
63 }
64 #endif
65 
66 }  // namespace
67 
ExceptionSnapshotWin()68 ExceptionSnapshotWin::ExceptionSnapshotWin()
69     : ExceptionSnapshot(),
70       context_union_(),
71       context_(),
72       codes_(),
73       extra_memory_(),
74       thread_id_(0),
75       exception_address_(0),
76       exception_flags_(0),
77       exception_code_(0),
78       initialized_() {
79 }
80 
~ExceptionSnapshotWin()81 ExceptionSnapshotWin::~ExceptionSnapshotWin() {
82 }
83 
Initialize(ProcessReaderWin * process_reader,DWORD thread_id,WinVMAddress exception_pointers_address)84 bool ExceptionSnapshotWin::Initialize(
85     ProcessReaderWin* process_reader,
86     DWORD thread_id,
87     WinVMAddress exception_pointers_address) {
88   INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
89 
90   const ProcessReaderWin::Thread* thread = nullptr;
91   for (const auto& loop_thread : process_reader->Threads()) {
92     if (thread_id == loop_thread.id) {
93       thread = &loop_thread;
94       break;
95     }
96   }
97 
98   if (!thread) {
99     LOG(ERROR) << "thread ID " << thread_id << " not found in process";
100     return false;
101   } else {
102     thread_id_ = thread_id;
103   }
104 
105 #if defined(ARCH_CPU_32_BITS)
106   const bool is_64_bit = false;
107 #elif defined(ARCH_CPU_64_BITS)
108   const bool is_64_bit = process_reader->Is64Bit();
109   if (is_64_bit) {
110     if (!InitializeFromExceptionPointers<EXCEPTION_RECORD64,
111                                          process_types::EXCEPTION_POINTERS64>(
112             process_reader,
113             exception_pointers_address,
114             thread_id,
115             &NativeContextToCPUContext64)) {
116       return false;
117     }
118   }
119 #endif
120 
121 #if !defined(ARCH_CPU_ARM64)
122   if (!is_64_bit) {
123     if (!InitializeFromExceptionPointers<EXCEPTION_RECORD32,
124                                          process_types::EXCEPTION_POINTERS32>(
125             process_reader,
126             exception_pointers_address,
127             thread_id,
128             &NativeContextToCPUContext32)) {
129       return false;
130     }
131   }
132 #endif
133 
134   CaptureMemoryDelegateWin capture_memory_delegate(
135       process_reader, *thread, &extra_memory_, nullptr);
136   CaptureMemory::PointedToByContext(context_, &capture_memory_delegate);
137 
138   INITIALIZATION_STATE_SET_VALID(initialized_);
139   return true;
140 }
141 
Context() const142 const CPUContext* ExceptionSnapshotWin::Context() const {
143   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
144   return &context_;
145 }
146 
ThreadID() const147 uint64_t ExceptionSnapshotWin::ThreadID() const {
148   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
149   return thread_id_;
150 }
151 
Exception() const152 uint32_t ExceptionSnapshotWin::Exception() const {
153   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
154   return exception_code_;
155 }
156 
ExceptionInfo() const157 uint32_t ExceptionSnapshotWin::ExceptionInfo() const {
158   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
159   return exception_flags_;
160 }
161 
ExceptionAddress() const162 uint64_t ExceptionSnapshotWin::ExceptionAddress() const {
163   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
164   return exception_address_;
165 }
166 
Codes() const167 const std::vector<uint64_t>& ExceptionSnapshotWin::Codes() const {
168   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
169   return codes_;
170 }
171 
ExtraMemory() const172 std::vector<const MemorySnapshot*> ExceptionSnapshotWin::ExtraMemory() const {
173   INITIALIZATION_STATE_DCHECK_VALID(initialized_);
174   std::vector<const MemorySnapshot*> result;
175   result.reserve(extra_memory_.size());
176   for (const auto& em : extra_memory_) {
177     result.push_back(em.get());
178   }
179   return result;
180 }
181 
182 template <class ExceptionRecordType,
183           class ExceptionPointersType,
184           class ContextType>
InitializeFromExceptionPointers(ProcessReaderWin * process_reader,WinVMAddress exception_pointers_address,DWORD exception_thread_id,void (* native_to_cpu_context)(const ContextType & context_record,CPUContext * context,CPUContextUnion * context_union))185 bool ExceptionSnapshotWin::InitializeFromExceptionPointers(
186     ProcessReaderWin* process_reader,
187     WinVMAddress exception_pointers_address,
188     DWORD exception_thread_id,
189     void (*native_to_cpu_context)(const ContextType& context_record,
190                                   CPUContext* context,
191                                   CPUContextUnion* context_union)) {
192   ExceptionPointersType exception_pointers;
193   if (!process_reader->Memory()->Read(exception_pointers_address,
194                                       sizeof(exception_pointers),
195                                       &exception_pointers)) {
196     LOG(ERROR) << "EXCEPTION_POINTERS read failed";
197     return false;
198   }
199   if (!exception_pointers.ExceptionRecord) {
200     LOG(ERROR) << "null ExceptionRecord";
201     return false;
202   }
203 
204   ExceptionRecordType first_record;
205   if (!process_reader->Memory()->Read(
206           static_cast<WinVMAddress>(exception_pointers.ExceptionRecord),
207           sizeof(first_record),
208           &first_record)) {
209     LOG(ERROR) << "ExceptionRecord";
210     return false;
211   }
212 
213   const bool triggered_by_client =
214       first_record.ExceptionCode == CrashpadClient::kTriggeredExceptionCode &&
215       first_record.NumberParameters == 2;
216   if (triggered_by_client)
217     process_reader->DecrementThreadSuspendCounts(exception_thread_id);
218 
219   if (triggered_by_client && first_record.ExceptionInformation[0] != 0) {
220     // This special exception code indicates that the target was crashed by
221     // another client calling CrashpadClient::DumpAndCrashTargetProcess(). In
222     // this case the parameters are a thread id and an exception code which we
223     // use to fabricate a new exception record.
224     using ArgumentType = decltype(first_record.ExceptionInformation[0]);
225     const ArgumentType blame_thread_id = first_record.ExceptionInformation[0];
226     exception_code_ = static_cast<DWORD>(first_record.ExceptionInformation[1]);
227     exception_flags_ = EXCEPTION_NONCONTINUABLE;
228     for (const auto& thread : process_reader->Threads()) {
229       if (thread.id == blame_thread_id) {
230         thread_id_ = blame_thread_id;
231         native_to_cpu_context(
232             *reinterpret_cast<const ContextType*>(&thread.context),
233             &context_,
234             &context_union_);
235         exception_address_ = context_.InstructionPointer();
236         break;
237       }
238     }
239 
240     if (exception_address_ == 0) {
241       LOG(WARNING) << "thread " << blame_thread_id << " not found";
242       return false;
243     }
244   } else {
245     // Normal case.
246     exception_code_ = first_record.ExceptionCode;
247     exception_flags_ = first_record.ExceptionFlags;
248     exception_address_ = first_record.ExceptionAddress;
249     for (DWORD i = 0; i < first_record.NumberParameters; ++i)
250       codes_.push_back(first_record.ExceptionInformation[i]);
251     if (first_record.ExceptionRecord) {
252       // https://crashpad.chromium.org/bug/43
253       LOG(WARNING) << "dropping chained ExceptionRecord";
254     }
255 
256     ContextType context_record;
257     if (!process_reader->Memory()->Read(
258             static_cast<WinVMAddress>(exception_pointers.ContextRecord),
259             sizeof(context_record),
260             &context_record)) {
261       LOG(ERROR) << "ContextRecord";
262       return false;
263     }
264 
265     native_to_cpu_context(context_record, &context_, &context_union_);
266   }
267 
268   return true;
269 }
270 
271 }  // namespace internal
272 }  // namespace crashpad
273