Chromium Code Reviews| Index: handler/win/registration_pipe_state.cc |
| diff --git a/handler/win/registration_pipe_state.cc b/handler/win/registration_pipe_state.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..958c8e98ecf64abcf86460c5589ca0860e6ee9bf |
| --- /dev/null |
| +++ b/handler/win/registration_pipe_state.cc |
| @@ -0,0 +1,284 @@ |
| +// 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 "handler/win/registration_pipe_state.h" |
| + |
| +#include <string.h> |
| +#include <vector> |
|
scottmg
2015/05/25 18:41:54
split C and C++ headers by a newline
erikwright (departed)
2015/05/25 19:42:12
Done.
|
| + |
| +#include "base/logging.h" |
| +#include "base/memory/scoped_ptr.h" |
| +#include "util/stdlib/pointer_container.h" |
| + |
| +namespace crashpad { |
| + |
| +RegistrationPipeState::RegistrationPipeState( |
| + ScopedFileHANDLE pipe, |
| + RegistrationServer::Delegate* delegate) |
| + : request_(), |
| + response_(), |
| + completion_handler_(nullptr), |
| + overlapped_(), |
| + event_(), |
| + pipe_(pipe.Pass()), |
| + waiting_for_close_(false), |
| + delegate_(delegate), |
| + get_named_pipe_client_process_id_proc_(nullptr) { |
| + HMODULE kernel_dll = GetModuleHandle(L"kernel32.dll"); |
| + if (kernel_dll) { |
| + get_named_pipe_client_process_id_proc_ = |
| + reinterpret_cast<decltype(GetNamedPipeClientProcessId)*>( |
| + GetProcAddress(kernel_dll, "GetNamedPipeClientProcessId")); |
| + } |
| +} |
| + |
| +RegistrationPipeState::~RegistrationPipeState() { |
| +} |
| + |
| +bool RegistrationPipeState::Initialize() { |
| + DCHECK(!event_.is_valid()); |
| + DCHECK(pipe_.is_valid()); |
| + |
| + event_.reset(CreateEvent(nullptr, true, false, nullptr)); |
| + |
| + if (!event_.is_valid()) { |
| + PLOG(ERROR) << "CreateEvent"; |
| + } else { |
| + overlapped_.hEvent = event_.get(); |
| + if (IssueConnect()) |
| + return true; |
| + } |
| + |
| + overlapped_.hEvent = nullptr; |
| + event_.reset(); |
| + pipe_.reset(); |
| + completion_handler_ = nullptr; |
| + |
| + return false; |
| +} |
| + |
| +void RegistrationPipeState::Stop() { |
| + DCHECK(pipe_.is_valid()); |
| + if (!CancelIo(pipe_.get())) |
| + PLOG(FATAL) << "CancelIo"; |
| +} |
| + |
| +bool RegistrationPipeState::OnCompletion() { |
| + AsyncCompletionHandler completion_handler = completion_handler_; |
| + completion_handler_ = nullptr; |
| + |
| + DWORD bytes_transferred = 0; |
| + BOOL success = GetOverlappedResult(pipe_.get(), |
| + &overlapped_, |
| + &bytes_transferred, |
| + false); // Do not wait. |
| + if (!success) { |
| + // ERROR_BROKEN_PIPE is expected when we are waiting for the client to close |
| + // the pipe (signaling that they are done reading the response). |
| + if (!waiting_for_close_ || GetLastError() != ERROR_BROKEN_PIPE) |
| + PLOG(ERROR) << "GetOverlappedResult"; |
| + } |
| + |
| + bool still_running = false; |
| + if (!ResetEvent(event_.get())) { |
| + PLOG(ERROR) << "ResetEvent"; |
| + } else if (!completion_handler) { |
| + NOTREACHED(); |
| + still_running = ResetConnection(); |
| + } else if (!success) { |
| + still_running = ResetConnection(); |
| + } else { |
| + still_running = (this->*completion_handler)(bytes_transferred); |
| + } |
| + |
| + if (!still_running) { |
| + overlapped_.hEvent = nullptr; |
| + event_.reset(); |
| + pipe_.reset(); |
| + completion_handler_ = nullptr; |
| + } else { |
| + DCHECK(completion_handler_); |
| + } |
| + |
| + return still_running; |
| +} |
| + |
| +bool RegistrationPipeState::OnConnectComplete(DWORD /* bytes_transferred */) { |
| + return IssueRead(); |
| +} |
| + |
| +bool RegistrationPipeState::OnReadComplete(DWORD bytes_transferred) { |
| + if (bytes_transferred != sizeof(request_)) { |
| + LOG(ERROR) << "Invalid message size: " << bytes_transferred; |
| + return ResetConnection(); |
| + } else { |
| + return HandleRequest(); |
| + } |
| +} |
| + |
| +bool RegistrationPipeState::OnWriteComplete(DWORD bytes_transferred) { |
| + if (bytes_transferred != sizeof(response_)) { |
| + LOG(ERROR) << "Incomplete write operation. Bytes written: " |
| + << bytes_transferred; |
| + } |
| + |
| + return IssueWaitForClientClose(); |
| +} |
| + |
| +bool RegistrationPipeState::OnWaitForClientCloseComplete( |
| + DWORD bytes_transferred) { |
| + LOG(ERROR) << "Unexpected extra data (" << bytes_transferred |
| + << " bytes) received from client."; |
| + return ResetConnection(); |
| +} |
| + |
| +bool RegistrationPipeState::IssueConnect() { |
| + if (ConnectNamedPipe(pipe_.get(), &overlapped_)) { |
| + return OnConnectComplete(0); // bytes_transferred (ignored) |
| + } else { |
| + DWORD result = GetLastError(); |
| + if (result == ERROR_PIPE_CONNECTED) { |
| + return OnConnectComplete(0); // bytes_transferred (ignored) |
| + } else if (result == ERROR_IO_PENDING) { |
| + completion_handler_ = &RegistrationPipeState::OnConnectComplete; |
| + return true; |
| + } else { |
| + PLOG(ERROR) << "ConnectNamedPipe"; |
| + return false; |
| + } |
| + } |
| +} |
| + |
| +bool RegistrationPipeState::IssueRead() { |
| + DWORD bytes_read = 0; |
| + if (ReadFile(pipe_.get(), |
| + &request_, |
| + sizeof(request_), |
| + &bytes_read, |
| + &overlapped_)) { |
| + return OnReadComplete(bytes_read); |
| + } else if (GetLastError() == ERROR_IO_PENDING) { |
| + completion_handler_ = &RegistrationPipeState::OnReadComplete; |
| + return true; |
| + } else { |
| + PLOG(ERROR) << "ReadFile"; |
| + return ResetConnection(); |
| + } |
| +} |
| + |
| +bool RegistrationPipeState::IssueWrite() { |
| + DWORD bytes_written = 0; |
| + if (WriteFile(pipe_.get(), |
| + &response_, |
| + sizeof(response_), |
| + &bytes_written, |
| + &overlapped_)) { |
| + return OnWriteComplete(bytes_written); |
| + } else if (GetLastError() == ERROR_IO_PENDING) { |
| + completion_handler_ = &RegistrationPipeState::OnWriteComplete; |
| + return true; |
| + } else { |
| + PLOG(ERROR) << "WriteFile"; |
| + return ResetConnection(); |
| + } |
| +} |
| + |
| +bool RegistrationPipeState::IssueWaitForClientClose() { |
| + waiting_for_close_ = true; |
| + DWORD bytes_read = 0; |
| + if (ReadFile(pipe_.get(), |
| + &request_, |
| + sizeof(request_), |
| + &bytes_read, |
| + &overlapped_)) { |
| + return OnWaitForClientCloseComplete(bytes_read); |
| + } else if (GetLastError() == ERROR_IO_PENDING) { |
| + completion_handler_ = &RegistrationPipeState::OnWaitForClientCloseComplete; |
| + return true; |
| + } else { |
| + PLOG(ERROR) << "ReadFile"; |
| + return ResetConnection(); |
| + } |
| +} |
| + |
| +bool RegistrationPipeState::HandleRequest() { |
| + if (get_named_pipe_client_process_id_proc_) { |
| + // On Vista+ we can verify that the client is who they claim to be, thus |
| + // preventing arbitrary processes from having us duplicate handles into |
| + // other processes. |
| + DWORD real_client_process_id = 0; |
| + if (!get_named_pipe_client_process_id_proc_(pipe_.get(), |
| + &real_client_process_id)) { |
| + PLOG(ERROR) << "GetNamedPipeClientProcessId"; |
| + } else if (real_client_process_id != request_.client_process_id) { |
| + LOG(ERROR) << "Client process ID from request (" |
| + << request_.client_process_id |
| + << ") does not match pipe client process ID (" |
| + << real_client_process_id << ")."; |
| + return ResetConnection(); |
| + } |
| + } |
| + |
| + ScopedKernelHANDLE client_process( |
| + OpenProcess(PROCESS_ALL_ACCESS, false, request_.client_process_id)); |
| + if (!client_process.is_valid()) { |
| + if (ImpersonateNamedPipeClient(pipe_.get())) { |
| + client_process.reset( |
| + OpenProcess(PROCESS_ALL_ACCESS, false, request_.client_process_id)); |
| + RevertToSelf(); |
| + } |
| + } |
| + |
| + if (!client_process.is_valid()) { |
| + LOG(ERROR) << "Failed to open client process."; |
| + return ResetConnection(); |
| + } |
| + |
| + memset(&response_, 0, sizeof(response_)); |
| + |
| + HANDLE request_report_event = nullptr; |
| + HANDLE report_complete_event = nullptr; |
| + |
| + if (!delegate_->RegisterClient(client_process.Pass(), |
| + request_.crashpad_info_address, |
| + &request_report_event, |
| + &report_complete_event)) { |
| + return ResetConnection(); |
| + } |
| + |
| + // A handle has at most 32 significant bits, though its type is void*. Thus we |
| + // truncate it here. An interesting exception is INVALID_HANDLE_VALUE, which |
| + // is '-1'. It is still safe to truncate it from 0xFFFFFFFFFFFFFFFF to |
| + // 0xFFFFFFFF, but a 64-bit client receiving that value must correctly sign |
| + // extend it. |
| + response_.request_report_event = |
| + reinterpret_cast<uint32_t>(request_report_event); |
| + response_.report_complete_event = |
| + reinterpret_cast<uint32_t>(report_complete_event); |
| + return IssueWrite(); |
| +} |
| + |
| +bool RegistrationPipeState::ResetConnection() { |
| + memset(&request_, 0, sizeof(request_)); |
| + waiting_for_close_ = false; |
| + |
| + if (!DisconnectNamedPipe(pipe_.get())) { |
| + PLOG(ERROR) << "DisconnectNamedPipe"; |
| + return false; |
| + } else { |
| + return IssueConnect(); |
| + } |
| +} |
| + |
| +} // namespace crashpad |