// Copyright 2015 The Crashpad Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "util/win/registration_protocol_win.h" #include #include #include "base/logging.h" #include "util/win/exception_handler_server.h" #include "util/win/scoped_handle.h" #include "util/win/scoped_local_alloc.h" namespace crashpad { bool SendToCrashHandlerServer(const base::string16& pipe_name, const ClientToServerMessage& message, ServerToClientMessage* response) { // Retry CreateFile() in a loop. If the handler isn’t actively waiting in // ConnectNamedPipe() on a pipe instance because it’s busy doing something // else, CreateFile() will fail with ERROR_PIPE_BUSY. WaitNamedPipe() waits // until a pipe instance is ready, but there’s no way to wait for this // condition and atomically open the client side of the pipe in a single // operation. CallNamedPipe() implements similar retry logic to this, also in // user-mode code. // // This loop is only intended to retry on ERROR_PIPE_BUSY. Notably, if the // handler is so lazy that it hasn’t even called CreateNamedPipe() yet, // CreateFile() will fail with ERROR_FILE_NOT_FOUND, and this function is // expected to fail without retrying anything. If the handler is started at // around the same time as its client, something external to this code must be // done to guarantee correct ordering. When the client starts the handler // itself, CrashpadClient::StartHandler() provides this synchronization. for (;;) { ScopedFileHANDLE pipe( CreateFile(pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, nullptr)); if (!pipe.is_valid()) { if (GetLastError() != ERROR_PIPE_BUSY) { PLOG(ERROR) << "CreateFile"; return false; } if (!WaitNamedPipe(pipe_name.c_str(), NMPWAIT_WAIT_FOREVER)) { PLOG(ERROR) << "WaitNamedPipe"; return false; } continue; } DWORD mode = PIPE_READMODE_MESSAGE; if (!SetNamedPipeHandleState(pipe.get(), &mode, nullptr, nullptr)) { PLOG(ERROR) << "SetNamedPipeHandleState"; return false; } DWORD bytes_read = 0; BOOL result = TransactNamedPipe( pipe.get(), // This is [in], but is incorrectly declared non-const. const_cast(&message), sizeof(message), response, sizeof(*response), &bytes_read, nullptr); if (!result) { PLOG(ERROR) << "TransactNamedPipe"; return false; } if (bytes_read != sizeof(*response)) { LOG(ERROR) << "TransactNamedPipe: expected " << sizeof(*response) << ", observed " << bytes_read; return false; } return true; } } HANDLE CreateNamedPipeInstance(const std::wstring& pipe_name, bool first_instance) { SECURITY_ATTRIBUTES security_attributes; SECURITY_ATTRIBUTES* security_attributes_pointer = nullptr; ScopedLocalAlloc scoped_sec_desc; if (first_instance) { // Pre-Vista does not have integrity levels. const DWORD version = GetVersion(); const DWORD major_version = LOBYTE(LOWORD(version)); const bool is_vista_or_later = major_version >= 6; if (is_vista_or_later) { // Mandatory Label, no ACE flags, no ObjectType, integrity level // untrusted. const wchar_t kSddl[] = L"S:(ML;;;;;S-1-16-0)"; PSECURITY_DESCRIPTOR sec_desc; PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor( kSddl, SDDL_REVISION_1, &sec_desc, nullptr)) << "ConvertStringSecurityDescriptorToSecurityDescriptor"; // Take ownership of the allocated SECURITY_DESCRIPTOR. scoped_sec_desc.reset(sec_desc); memset(&security_attributes, 0, sizeof(security_attributes)); security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); security_attributes.lpSecurityDescriptor = sec_desc; security_attributes.bInheritHandle = TRUE; security_attributes_pointer = &security_attributes; } } return CreateNamedPipe( pipe_name.c_str(), PIPE_ACCESS_DUPLEX | (first_instance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0), PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, ExceptionHandlerServer::kPipeInstances, 512, 512, 0, security_attributes_pointer); } } // namespace crashpad