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