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 "util/win/exception_handler_server.h"
16
17 #include <stdint.h>
18 #include <string.h>
19 #include <sys/types.h>
20
21 #include <utility>
22
23 #include "base/logging.h"
24 #include "base/numerics/safe_conversions.h"
25 #include "base/rand_util.h"
26 #include "base/stl_util.h"
27 #include "base/strings/stringprintf.h"
28 #include "base/strings/utf_string_conversions.h"
29 #include "util/file/file_writer.h"
30 #include "util/misc/tri_state.h"
31 #include "util/misc/uuid.h"
32 #include "util/win/get_function.h"
33 #include "util/win/handle.h"
34 #include "util/win/registration_protocol_win.h"
35 #include "util/win/safe_terminate_process.h"
36 #include "util/win/xp_compat.h"
37
38 namespace crashpad {
39
40 namespace {
41
GetNamedPipeClientProcessIdFunction()42 decltype(GetNamedPipeClientProcessId)* GetNamedPipeClientProcessIdFunction() {
43 static const auto get_named_pipe_client_process_id =
44 GET_FUNCTION(L"kernel32.dll", ::GetNamedPipeClientProcessId);
45 return get_named_pipe_client_process_id;
46 }
47
DuplicateEvent(HANDLE process,HANDLE event)48 HANDLE DuplicateEvent(HANDLE process, HANDLE event) {
49 HANDLE handle;
50 if (DuplicateHandle(GetCurrentProcess(),
51 event,
52 process,
53 &handle,
54 SYNCHRONIZE | EVENT_MODIFY_STATE,
55 false,
56 0)) {
57 return handle;
58 }
59 return nullptr;
60 }
61
62 } // namespace
63
64 namespace internal {
65
66 //! \brief Context information for the named pipe handler threads.
67 class PipeServiceContext {
68 public:
PipeServiceContext(HANDLE port,HANDLE pipe,ExceptionHandlerServer::Delegate * delegate,base::Lock * clients_lock,std::set<internal::ClientData * > * clients,uint64_t shutdown_token)69 PipeServiceContext(HANDLE port,
70 HANDLE pipe,
71 ExceptionHandlerServer::Delegate* delegate,
72 base::Lock* clients_lock,
73 std::set<internal::ClientData*>* clients,
74 uint64_t shutdown_token)
75 : port_(port),
76 pipe_(pipe),
77 delegate_(delegate),
78 clients_lock_(clients_lock),
79 clients_(clients),
80 shutdown_token_(shutdown_token) {}
81
port() const82 HANDLE port() const { return port_; }
pipe() const83 HANDLE pipe() const { return pipe_.get(); }
delegate() const84 ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
clients_lock() const85 base::Lock* clients_lock() const { return clients_lock_; }
clients() const86 std::set<internal::ClientData*>* clients() const { return clients_; }
shutdown_token() const87 uint64_t shutdown_token() const { return shutdown_token_; }
88
89 private:
90 HANDLE port_; // weak
91 ScopedKernelHANDLE pipe_;
92 ExceptionHandlerServer::Delegate* delegate_; // weak
93 base::Lock* clients_lock_; // weak
94 std::set<internal::ClientData*>* clients_; // weak
95 uint64_t shutdown_token_;
96
97 DISALLOW_COPY_AND_ASSIGN(PipeServiceContext);
98 };
99
100 //! \brief The context data for registered threadpool waits.
101 //!
102 //! This object must be created and destroyed on the main thread. Access must be
103 //! guarded by use of the lock() with the exception of the threadpool wait
104 //! variables which are accessed only by the main thread.
105 class ClientData {
106 public:
ClientData(HANDLE port,ExceptionHandlerServer::Delegate * delegate,ScopedKernelHANDLE process,ScopedKernelHANDLE crash_dump_requested_event,ScopedKernelHANDLE non_crash_dump_requested_event,ScopedKernelHANDLE non_crash_dump_completed_event,WinVMAddress crash_exception_information_address,WinVMAddress non_crash_exception_information_address,WinVMAddress debug_critical_section_address,WAITORTIMERCALLBACK crash_dump_request_callback,WAITORTIMERCALLBACK non_crash_dump_request_callback,WAITORTIMERCALLBACK process_end_callback)107 ClientData(HANDLE port,
108 ExceptionHandlerServer::Delegate* delegate,
109 ScopedKernelHANDLE process,
110 ScopedKernelHANDLE crash_dump_requested_event,
111 ScopedKernelHANDLE non_crash_dump_requested_event,
112 ScopedKernelHANDLE non_crash_dump_completed_event,
113 WinVMAddress crash_exception_information_address,
114 WinVMAddress non_crash_exception_information_address,
115 WinVMAddress debug_critical_section_address,
116 WAITORTIMERCALLBACK crash_dump_request_callback,
117 WAITORTIMERCALLBACK non_crash_dump_request_callback,
118 WAITORTIMERCALLBACK process_end_callback)
119 : crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
120 non_crash_dump_request_thread_pool_wait_(INVALID_HANDLE_VALUE),
121 process_end_thread_pool_wait_(INVALID_HANDLE_VALUE),
122 lock_(),
123 port_(port),
124 delegate_(delegate),
125 crash_dump_requested_event_(std::move(crash_dump_requested_event)),
126 non_crash_dump_requested_event_(
127 std::move(non_crash_dump_requested_event)),
128 non_crash_dump_completed_event_(
129 std::move(non_crash_dump_completed_event)),
130 process_(std::move(process)),
131 crash_exception_information_address_(
132 crash_exception_information_address),
133 non_crash_exception_information_address_(
134 non_crash_exception_information_address),
135 debug_critical_section_address_(debug_critical_section_address) {
136 RegisterThreadPoolWaits(crash_dump_request_callback,
137 non_crash_dump_request_callback,
138 process_end_callback);
139 }
140
~ClientData()141 ~ClientData() {
142 // It is important that this only access the threadpool waits (it's called
143 // from the main thread) until the waits are unregistered, to ensure that
144 // any outstanding callbacks are complete.
145 UnregisterThreadPoolWaits();
146 }
147
lock()148 base::Lock* lock() { return &lock_; }
port() const149 HANDLE port() const { return port_; }
delegate() const150 ExceptionHandlerServer::Delegate* delegate() const { return delegate_; }
crash_dump_requested_event() const151 HANDLE crash_dump_requested_event() const {
152 return crash_dump_requested_event_.get();
153 }
non_crash_dump_requested_event() const154 HANDLE non_crash_dump_requested_event() const {
155 return non_crash_dump_requested_event_.get();
156 }
non_crash_dump_completed_event() const157 HANDLE non_crash_dump_completed_event() const {
158 return non_crash_dump_completed_event_.get();
159 }
crash_exception_information_address() const160 WinVMAddress crash_exception_information_address() const {
161 return crash_exception_information_address_;
162 }
non_crash_exception_information_address() const163 WinVMAddress non_crash_exception_information_address() const {
164 return non_crash_exception_information_address_;
165 }
debug_critical_section_address() const166 WinVMAddress debug_critical_section_address() const {
167 return debug_critical_section_address_;
168 }
process() const169 HANDLE process() const { return process_.get(); }
170
171 private:
RegisterThreadPoolWaits(WAITORTIMERCALLBACK crash_dump_request_callback,WAITORTIMERCALLBACK non_crash_dump_request_callback,WAITORTIMERCALLBACK process_end_callback)172 void RegisterThreadPoolWaits(
173 WAITORTIMERCALLBACK crash_dump_request_callback,
174 WAITORTIMERCALLBACK non_crash_dump_request_callback,
175 WAITORTIMERCALLBACK process_end_callback) {
176 if (!RegisterWaitForSingleObject(&crash_dump_request_thread_pool_wait_,
177 crash_dump_requested_event_.get(),
178 crash_dump_request_callback,
179 this,
180 INFINITE,
181 WT_EXECUTEDEFAULT)) {
182 LOG(ERROR) << "RegisterWaitForSingleObject crash dump requested";
183 }
184
185 if (!RegisterWaitForSingleObject(&non_crash_dump_request_thread_pool_wait_,
186 non_crash_dump_requested_event_.get(),
187 non_crash_dump_request_callback,
188 this,
189 INFINITE,
190 WT_EXECUTEDEFAULT)) {
191 LOG(ERROR) << "RegisterWaitForSingleObject non-crash dump requested";
192 }
193
194 if (!RegisterWaitForSingleObject(&process_end_thread_pool_wait_,
195 process_.get(),
196 process_end_callback,
197 this,
198 INFINITE,
199 WT_EXECUTEONLYONCE)) {
200 LOG(ERROR) << "RegisterWaitForSingleObject process end";
201 }
202 }
203
204 // This blocks until outstanding calls complete so that we know it's safe to
205 // delete this object. Because of this, it must be executed on the main
206 // thread, not a threadpool thread.
UnregisterThreadPoolWaits()207 void UnregisterThreadPoolWaits() {
208 UnregisterWaitEx(crash_dump_request_thread_pool_wait_,
209 INVALID_HANDLE_VALUE);
210 crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
211 UnregisterWaitEx(non_crash_dump_request_thread_pool_wait_,
212 INVALID_HANDLE_VALUE);
213 non_crash_dump_request_thread_pool_wait_ = INVALID_HANDLE_VALUE;
214 UnregisterWaitEx(process_end_thread_pool_wait_, INVALID_HANDLE_VALUE);
215 process_end_thread_pool_wait_ = INVALID_HANDLE_VALUE;
216 }
217
218 // These are only accessed on the main thread.
219 HANDLE crash_dump_request_thread_pool_wait_;
220 HANDLE non_crash_dump_request_thread_pool_wait_;
221 HANDLE process_end_thread_pool_wait_;
222
223 base::Lock lock_;
224 // Access to these fields must be guarded by lock_.
225 HANDLE port_; // weak
226 ExceptionHandlerServer::Delegate* delegate_; // weak
227 ScopedKernelHANDLE crash_dump_requested_event_;
228 ScopedKernelHANDLE non_crash_dump_requested_event_;
229 ScopedKernelHANDLE non_crash_dump_completed_event_;
230 ScopedKernelHANDLE process_;
231 WinVMAddress crash_exception_information_address_;
232 WinVMAddress non_crash_exception_information_address_;
233 WinVMAddress debug_critical_section_address_;
234
235 DISALLOW_COPY_AND_ASSIGN(ClientData);
236 };
237
238 } // namespace internal
239
~Delegate()240 ExceptionHandlerServer::Delegate::~Delegate() {
241 }
242
ExceptionHandlerServer(bool persistent)243 ExceptionHandlerServer::ExceptionHandlerServer(bool persistent)
244 : pipe_name_(),
245 port_(CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1)),
246 first_pipe_instance_(),
247 clients_lock_(),
248 clients_(),
249 persistent_(persistent) {
250 }
251
~ExceptionHandlerServer()252 ExceptionHandlerServer::~ExceptionHandlerServer() {
253 }
254
SetPipeName(const std::wstring & pipe_name)255 void ExceptionHandlerServer::SetPipeName(const std::wstring& pipe_name) {
256 DCHECK(pipe_name_.empty());
257 DCHECK(!pipe_name.empty());
258
259 pipe_name_ = pipe_name;
260 }
261
InitializeWithInheritedDataForInitialClient(const InitialClientData & initial_client_data,Delegate * delegate)262 void ExceptionHandlerServer::InitializeWithInheritedDataForInitialClient(
263 const InitialClientData& initial_client_data,
264 Delegate* delegate) {
265 DCHECK(pipe_name_.empty());
266 DCHECK(!first_pipe_instance_.is_valid());
267
268 first_pipe_instance_.reset(initial_client_data.first_pipe_instance());
269
270 // TODO(scottmg): Vista+. Might need to pass through or possibly find an Nt*.
271 size_t bytes = sizeof(wchar_t) * _MAX_PATH + sizeof(FILE_NAME_INFO);
272 std::unique_ptr<uint8_t[]> data(new uint8_t[bytes]);
273 if (!GetFileInformationByHandleEx(first_pipe_instance_.get(),
274 FileNameInfo,
275 data.get(),
276 static_cast<DWORD>(bytes))) {
277 PLOG(FATAL) << "GetFileInformationByHandleEx";
278 }
279 FILE_NAME_INFO* file_name_info =
280 reinterpret_cast<FILE_NAME_INFO*>(data.get());
281 pipe_name_ =
282 L"\\\\.\\pipe" + std::wstring(file_name_info->FileName,
283 file_name_info->FileNameLength /
284 sizeof(file_name_info->FileName[0]));
285
286 {
287 base::AutoLock lock(clients_lock_);
288 internal::ClientData* client = new internal::ClientData(
289 port_.get(),
290 delegate,
291 ScopedKernelHANDLE(initial_client_data.client_process()),
292 ScopedKernelHANDLE(initial_client_data.request_crash_dump()),
293 ScopedKernelHANDLE(initial_client_data.request_non_crash_dump()),
294 ScopedKernelHANDLE(initial_client_data.non_crash_dump_completed()),
295 initial_client_data.crash_exception_information(),
296 initial_client_data.non_crash_exception_information(),
297 initial_client_data.debug_critical_section_address(),
298 &OnCrashDumpEvent,
299 &OnNonCrashDumpEvent,
300 &OnProcessEnd);
301 clients_.insert(client);
302 }
303 }
304
Run(Delegate * delegate)305 void ExceptionHandlerServer::Run(Delegate* delegate) {
306 uint64_t shutdown_token = base::RandUint64();
307 ScopedKernelHANDLE thread_handles[kPipeInstances];
308 for (size_t i = 0; i < base::size(thread_handles); ++i) {
309 HANDLE pipe;
310 if (first_pipe_instance_.is_valid()) {
311 pipe = first_pipe_instance_.release();
312 } else {
313 pipe = CreateNamedPipeInstance(pipe_name_, i == 0);
314 PCHECK(pipe != INVALID_HANDLE_VALUE) << "CreateNamedPipe";
315 }
316
317 // Ownership of this object (and the pipe instance) is given to the new
318 // thread. We close the thread handles at the end of the scope. They clean
319 // up the context object and the pipe instance on termination.
320 internal::PipeServiceContext* context =
321 new internal::PipeServiceContext(port_.get(),
322 pipe,
323 delegate,
324 &clients_lock_,
325 &clients_,
326 shutdown_token);
327 thread_handles[i].reset(
328 CreateThread(nullptr, 0, &PipeServiceProc, context, 0, nullptr));
329 PCHECK(thread_handles[i].is_valid()) << "CreateThread";
330 }
331
332 delegate->ExceptionHandlerServerStarted();
333
334 // This is the main loop of the server. Most work is done on the threadpool,
335 // other than process end handling which is posted back to this main thread,
336 // as we must unregister the threadpool waits here.
337 for (;;) {
338 OVERLAPPED* ov = nullptr;
339 ULONG_PTR key = 0;
340 DWORD bytes = 0;
341 GetQueuedCompletionStatus(port_.get(), &bytes, &key, &ov, INFINITE);
342 if (!key) {
343 // Shutting down.
344 break;
345 }
346
347 // Otherwise, this is a request to unregister and destroy the given client.
348 // delete'ing the ClientData blocks in UnregisterWaitEx to ensure all
349 // outstanding threadpool waits are complete. This is important because the
350 // process handle can be signalled *before* the dump request is signalled.
351 internal::ClientData* client = reinterpret_cast<internal::ClientData*>(key);
352 base::AutoLock lock(clients_lock_);
353 clients_.erase(client);
354 delete client;
355 if (!persistent_ && clients_.empty())
356 break;
357 }
358
359 // Signal to the named pipe instances that they should terminate.
360 for (size_t i = 0; i < base::size(thread_handles); ++i) {
361 ClientToServerMessage message;
362 memset(&message, 0, sizeof(message));
363 message.type = ClientToServerMessage::kShutdown;
364 message.shutdown.token = shutdown_token;
365 ServerToClientMessage response;
366 SendToCrashHandlerServer(pipe_name_,
367 reinterpret_cast<ClientToServerMessage&>(message),
368 &response);
369 }
370
371 for (auto& handle : thread_handles)
372 WaitForSingleObject(handle.get(), INFINITE);
373
374 // Deleting ClientData does a blocking wait until the threadpool executions
375 // have terminated when unregistering them.
376 {
377 base::AutoLock lock(clients_lock_);
378 for (auto* client : clients_)
379 delete client;
380 clients_.clear();
381 }
382 }
383
Stop()384 void ExceptionHandlerServer::Stop() {
385 // Post a null key (third argument) to trigger shutdown.
386 PostQueuedCompletionStatus(port_.get(), 0, 0, nullptr);
387 }
388
389 // This function must be called with service_context.pipe() already connected to
390 // a client pipe. It exchanges data with the client and adds a ClientData record
391 // to service_context->clients().
392 //
393 // static
ServiceClientConnection(const internal::PipeServiceContext & service_context)394 bool ExceptionHandlerServer::ServiceClientConnection(
395 const internal::PipeServiceContext& service_context) {
396 ClientToServerMessage message;
397
398 if (!LoggingReadFileExactly(
399 service_context.pipe(), &message, sizeof(message)))
400 return false;
401
402 switch (message.type) {
403 case ClientToServerMessage::kShutdown: {
404 if (message.shutdown.token != service_context.shutdown_token()) {
405 LOG(ERROR) << "forged shutdown request, got: "
406 << message.shutdown.token;
407 return false;
408 }
409 ServerToClientMessage shutdown_response = {};
410 LoggingWriteFile(service_context.pipe(),
411 &shutdown_response,
412 sizeof(shutdown_response));
413 return true;
414 }
415
416 case ClientToServerMessage::kPing: {
417 // No action required, the fact that the message was processed is
418 // sufficient.
419 ServerToClientMessage shutdown_response = {};
420 LoggingWriteFile(service_context.pipe(),
421 &shutdown_response,
422 sizeof(shutdown_response));
423 return false;
424 }
425
426 case ClientToServerMessage::kRegister:
427 // Handled below.
428 break;
429
430 default:
431 LOG(ERROR) << "unhandled message type: " << message.type;
432 return false;
433 }
434
435 if (message.registration.version != RegistrationRequest::kMessageVersion) {
436 LOG(ERROR) << "unexpected version. got: " << message.registration.version
437 << " expecting: " << RegistrationRequest::kMessageVersion;
438 return false;
439 }
440
441 decltype(GetNamedPipeClientProcessId)* get_named_pipe_client_process_id =
442 GetNamedPipeClientProcessIdFunction();
443 if (get_named_pipe_client_process_id) {
444 // GetNamedPipeClientProcessId is only available on Vista+.
445 DWORD real_pid = 0;
446 if (get_named_pipe_client_process_id(service_context.pipe(), &real_pid) &&
447 message.registration.client_process_id != real_pid) {
448 LOG(ERROR) << "forged client pid, real pid: " << real_pid
449 << ", got: " << message.registration.client_process_id;
450 return false;
451 }
452 }
453
454 // We attempt to open the process as us. This is the main case that should
455 // almost always succeed as the server will generally be more privileged. If
456 // we're running as a different user, it may be that we will fail to open
457 // the process, but the client will be able to, so we make a second attempt
458 // having impersonated the client.
459 HANDLE client_process = OpenProcess(
460 kXPProcessAllAccess, false, message.registration.client_process_id);
461 if (!client_process) {
462 if (!ImpersonateNamedPipeClient(service_context.pipe())) {
463 PLOG(ERROR) << "ImpersonateNamedPipeClient";
464 return false;
465 }
466 client_process = OpenProcess(
467 kXPProcessAllAccess, false, message.registration.client_process_id);
468 PCHECK(RevertToSelf());
469 if (!client_process) {
470 LOG(ERROR) << "failed to open " << message.registration.client_process_id;
471 return false;
472 }
473 }
474
475 internal::ClientData* client;
476 {
477 base::AutoLock lock(*service_context.clients_lock());
478 client = new internal::ClientData(
479 service_context.port(),
480 service_context.delegate(),
481 ScopedKernelHANDLE(client_process),
482 ScopedKernelHANDLE(
483 CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
484 ScopedKernelHANDLE(
485 CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
486 ScopedKernelHANDLE(
487 CreateEvent(nullptr, false /* auto reset */, false, nullptr)),
488 message.registration.crash_exception_information,
489 message.registration.non_crash_exception_information,
490 message.registration.critical_section_address,
491 &OnCrashDumpEvent,
492 &OnNonCrashDumpEvent,
493 &OnProcessEnd);
494 service_context.clients()->insert(client);
495 }
496
497 // Duplicate the events back to the client so they can request a dump.
498 ServerToClientMessage response;
499 response.registration.request_crash_dump_event =
500 HandleToInt(DuplicateEvent(
501 client->process(), client->crash_dump_requested_event()));
502 response.registration.request_non_crash_dump_event =
503 HandleToInt(DuplicateEvent(
504 client->process(), client->non_crash_dump_requested_event()));
505 response.registration.non_crash_dump_completed_event =
506 HandleToInt(DuplicateEvent(
507 client->process(), client->non_crash_dump_completed_event()));
508
509 if (!LoggingWriteFile(service_context.pipe(), &response, sizeof(response)))
510 return false;
511
512 return false;
513 }
514
515 // static
PipeServiceProc(void * ctx)516 DWORD __stdcall ExceptionHandlerServer::PipeServiceProc(void* ctx) {
517 internal::PipeServiceContext* service_context =
518 reinterpret_cast<internal::PipeServiceContext*>(ctx);
519 DCHECK(service_context);
520
521 for (;;) {
522 bool ret = !!ConnectNamedPipe(service_context->pipe(), nullptr);
523 if (!ret && GetLastError() != ERROR_PIPE_CONNECTED) {
524 PLOG(ERROR) << "ConnectNamedPipe";
525 } else if (ServiceClientConnection(*service_context)) {
526 break;
527 }
528 DisconnectNamedPipe(service_context->pipe());
529 }
530
531 delete service_context;
532
533 return 0;
534 }
535
536 // static
OnCrashDumpEvent(void * ctx,BOOLEAN)537 void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) {
538 // This function is executed on the thread pool.
539 internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
540 base::AutoLock lock(*client->lock());
541
542 // Capture the exception.
543 unsigned int exit_code = client->delegate()->ExceptionHandlerServerException(
544 client->process(),
545 client->crash_exception_information_address(),
546 client->debug_critical_section_address());
547
548 SafeTerminateProcess(client->process(), exit_code);
549 }
550
551 // static
OnNonCrashDumpEvent(void * ctx,BOOLEAN)552 void __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) {
553 // This function is executed on the thread pool.
554 internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
555 base::AutoLock lock(*client->lock());
556
557 // Capture the exception.
558 client->delegate()->ExceptionHandlerServerException(
559 client->process(),
560 client->non_crash_exception_information_address(),
561 client->debug_critical_section_address());
562
563 bool result = !!SetEvent(client->non_crash_dump_completed_event());
564 PLOG_IF(ERROR, !result) << "SetEvent";
565 }
566
567 // static
OnProcessEnd(void * ctx,BOOLEAN)568 void __stdcall ExceptionHandlerServer::OnProcessEnd(void* ctx, BOOLEAN) {
569 // This function is executed on the thread pool.
570 internal::ClientData* client = reinterpret_cast<internal::ClientData*>(ctx);
571 base::AutoLock lock(*client->lock());
572
573 // Post back to the main thread to have it delete this client record.
574 PostQueuedCompletionStatus(client->port(), 0, ULONG_PTR(client), nullptr);
575 }
576
577 } // namespace crashpad
578