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