Index: ipc/ipc_channel_win.cc |
diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc |
deleted file mode 100644 |
index f59b22ae4b8c81dd41d99db1aca851e3ab79839e..0000000000000000000000000000000000000000 |
--- a/ipc/ipc_channel_win.cc |
+++ /dev/null |
@@ -1,616 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "ipc/ipc_channel_win.h" |
- |
-#include <windows.h> |
-#include <stddef.h> |
-#include <stdint.h> |
- |
-#include "base/auto_reset.h" |
-#include "base/bind.h" |
-#include "base/compiler_specific.h" |
-#include "base/logging.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/pickle.h" |
-#include "base/process/process_handle.h" |
-#include "base/rand_util.h" |
-#include "base/single_thread_task_runner.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/utf_string_conversions.h" |
-#include "base/threading/thread_checker.h" |
-#include "base/threading/thread_task_runner_handle.h" |
-#include "base/win/scoped_handle.h" |
-#include "ipc/attachment_broker.h" |
-#include "ipc/ipc_listener.h" |
-#include "ipc/ipc_logging.h" |
-#include "ipc/ipc_message_attachment_set.h" |
-#include "ipc/ipc_message_utils.h" |
- |
-namespace IPC { |
- |
-ChannelWin::State::State() = default; |
- |
-ChannelWin::State::~State() { |
- static_assert(offsetof(ChannelWin::State, context) == 0, |
- "ChannelWin::State should have context as its first data" |
- "member."); |
-} |
- |
-ChannelWin::ChannelWin(const IPC::ChannelHandle& channel_handle, |
- Mode mode, |
- Listener* listener) |
- : ChannelReader(listener), |
- peer_pid_(base::kNullProcessId), |
- waiting_connect_(mode & MODE_SERVER_FLAG), |
- processing_incoming_(false), |
- validate_client_(false), |
- client_secret_(0), |
- weak_factory_(this) { |
- CreatePipe(channel_handle, mode); |
-} |
- |
-ChannelWin::~ChannelWin() { |
- CleanUp(); |
- Close(); |
-} |
- |
-void ChannelWin::Close() { |
- if (thread_check_.get()) |
- DCHECK(thread_check_->CalledOnValidThread()); |
- |
- if (input_state_.is_pending || output_state_.is_pending) |
- CancelIo(pipe_.Get()); |
- |
- // Closing the handle at this point prevents us from issuing more requests |
- // form OnIOCompleted(). |
- if (pipe_.IsValid()) |
- pipe_.Close(); |
- |
- // Make sure all IO has completed. |
- while (input_state_.is_pending || output_state_.is_pending) { |
- base::MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this); |
- } |
- |
- while (!output_queue_.empty()) { |
- OutputElement* element = output_queue_.front(); |
- output_queue_.pop(); |
- delete element; |
- } |
-} |
- |
-bool ChannelWin::Send(Message* message) { |
- DCHECK(thread_check_->CalledOnValidThread()); |
- DVLOG(2) << "sending message @" << message << " on channel @" << this |
- << " with type " << message->type() |
- << " (" << output_queue_.size() << " in queue)"; |
- |
- if (!prelim_queue_.empty()) { |
- prelim_queue_.push(message); |
- return true; |
- } |
- |
- if (message->HasBrokerableAttachments() && |
- peer_pid_ == base::kNullProcessId) { |
- prelim_queue_.push(message); |
- return true; |
- } |
- |
- return ProcessMessageForDelivery(message); |
-} |
- |
-bool ChannelWin::ProcessMessageForDelivery(Message* message) { |
- // Sending a brokerable attachment requires a call to Channel::Send(), so |
- // both Send() and ProcessMessageForDelivery() may be re-entrant. |
- if (message->HasBrokerableAttachments()) { |
- DCHECK(GetAttachmentBroker()); |
- DCHECK(peer_pid_ != base::kNullProcessId); |
- for (const scoped_refptr<IPC::BrokerableAttachment>& attachment : |
- message->attachment_set()->GetBrokerableAttachments()) { |
- if (!GetAttachmentBroker()->SendAttachmentToProcess(attachment, |
- peer_pid_)) { |
- delete message; |
- return false; |
- } |
- } |
- } |
- |
-#ifdef IPC_MESSAGE_LOG_ENABLED |
- Logging::GetInstance()->OnSendMessage(message, ""); |
-#endif |
- |
- TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"), |
- "ChannelWin::ProcessMessageForDelivery", |
- message->flags(), |
- TRACE_EVENT_FLAG_FLOW_OUT); |
- |
- // |output_queue_| takes ownership of |message|. |
- OutputElement* element = new OutputElement(message); |
- output_queue_.push(element); |
- |
-#if USE_ATTACHMENT_BROKER |
- if (message->HasBrokerableAttachments()) { |
- // |output_queue_| takes ownership of |ids.buffer|. |
- Message::SerializedAttachmentIds ids = |
- message->SerializedIdsOfBrokerableAttachments(); |
- output_queue_.push(new OutputElement(ids.buffer, ids.size)); |
- } |
-#endif |
- |
- // ensure waiting to write |
- if (!waiting_connect_) { |
- if (!output_state_.is_pending) { |
- if (!ProcessOutgoingMessages(NULL, 0)) |
- return false; |
- } |
- } |
- |
- return true; |
-} |
- |
-void ChannelWin::FlushPrelimQueue() { |
- DCHECK_NE(peer_pid_, base::kNullProcessId); |
- |
- // Due to the possibly re-entrant nature of ProcessMessageForDelivery(), it |
- // is critical that |prelim_queue_| appears empty. |
- std::queue<Message*> prelim_queue; |
- prelim_queue_.swap(prelim_queue); |
- |
- while (!prelim_queue.empty()) { |
- Message* m = prelim_queue.front(); |
- bool success = ProcessMessageForDelivery(m); |
- prelim_queue.pop(); |
- |
- if (!success) |
- break; |
- } |
- |
- // Delete any unprocessed messages. |
- while (!prelim_queue.empty()) { |
- Message* m = prelim_queue.front(); |
- delete m; |
- prelim_queue.pop(); |
- } |
-} |
- |
-AttachmentBroker* ChannelWin::GetAttachmentBroker() { |
- return AttachmentBroker::GetGlobal(); |
-} |
- |
-base::ProcessId ChannelWin::GetPeerPID() const { |
- return peer_pid_; |
-} |
- |
-base::ProcessId ChannelWin::GetSelfPID() const { |
- return GetCurrentProcessId(); |
-} |
- |
-// static |
-bool ChannelWin::IsNamedServerInitialized( |
- const std::string& channel_id) { |
- if (WaitNamedPipe(PipeName(channel_id, NULL).c_str(), 1)) |
- return true; |
- // If ERROR_SEM_TIMEOUT occurred, the pipe exists but is handling another |
- // connection. |
- return GetLastError() == ERROR_SEM_TIMEOUT; |
-} |
- |
-ChannelWin::ReadState ChannelWin::ReadData( |
- char* buffer, |
- int buffer_len, |
- int* /* bytes_read */) { |
- if (!pipe_.IsValid()) |
- return READ_FAILED; |
- |
- DWORD bytes_read = 0; |
- BOOL ok = ReadFile(pipe_.Get(), buffer, buffer_len, |
- &bytes_read, &input_state_.context.overlapped); |
- if (!ok) { |
- DWORD err = GetLastError(); |
- if (err == ERROR_IO_PENDING) { |
- input_state_.is_pending = true; |
- return READ_PENDING; |
- } |
- LOG(ERROR) << "pipe error: " << err; |
- return READ_FAILED; |
- } |
- |
- // We could return READ_SUCCEEDED here. But the way that this code is |
- // structured we instead go back to the message loop. Our completion port |
- // will be signalled even in the "synchronously completed" state. |
- // |
- // This allows us to potentially process some outgoing messages and |
- // interleave other work on this thread when we're getting hammered with |
- // input messages. Potentially, this could be tuned to be more efficient |
- // with some testing. |
- input_state_.is_pending = true; |
- return READ_PENDING; |
-} |
- |
-bool ChannelWin::ShouldDispatchInputMessage(Message* msg) { |
- // Make sure we get a hello when client validation is required. |
- if (validate_client_) |
- return IsHelloMessage(*msg); |
- return true; |
-} |
- |
-bool ChannelWin::GetNonBrokeredAttachments(Message* msg) { |
- return true; |
-} |
- |
-void ChannelWin::HandleInternalMessage(const Message& msg) { |
- DCHECK_EQ(msg.type(), static_cast<unsigned>(Channel::HELLO_MESSAGE_TYPE)); |
- // The hello message contains one parameter containing the PID. |
- base::PickleIterator it(msg); |
- int32_t claimed_pid; |
- bool failed = !it.ReadInt(&claimed_pid); |
- |
- if (!failed && validate_client_) { |
- int32_t secret; |
- failed = it.ReadInt(&secret) ? (secret != client_secret_) : true; |
- } |
- |
- if (failed) { |
- NOTREACHED(); |
- Close(); |
- listener()->OnChannelError(); |
- return; |
- } |
- |
- peer_pid_ = claimed_pid; |
- // Validation completed. |
- validate_client_ = false; |
- |
- listener()->OnChannelConnected(claimed_pid); |
- |
- FlushPrelimQueue(); |
- |
- if (IsAttachmentBrokerEndpoint() && |
- AttachmentBroker::GetGlobal()->IsPrivilegedBroker()) { |
- AttachmentBroker::GetGlobal()->ReceivedPeerPid(claimed_pid); |
- } |
-} |
- |
-base::ProcessId ChannelWin::GetSenderPID() { |
- return GetPeerPID(); |
-} |
- |
-bool ChannelWin::IsAttachmentBrokerEndpoint() { |
- return is_attachment_broker_endpoint(); |
-} |
- |
-bool ChannelWin::DidEmptyInputBuffers() { |
- // We don't need to do anything here. |
- return true; |
-} |
- |
-// static |
-const base::string16 ChannelWin::PipeName(const std::string& channel_id, |
- int32_t* secret) { |
- std::string name("\\\\.\\pipe\\chrome."); |
- |
- // Prevent the shared secret from ending up in the pipe name. |
- size_t index = channel_id.find_first_of('\\'); |
- if (index != std::string::npos) { |
- if (secret) // Retrieve the secret if asked for. |
- base::StringToInt(channel_id.substr(index + 1), secret); |
- return base::ASCIIToUTF16(name.append(channel_id.substr(0, index - 1))); |
- } |
- |
- // This case is here to support predictable named pipes in tests. |
- if (secret) |
- *secret = 0; |
- return base::ASCIIToUTF16(name.append(channel_id)); |
-} |
- |
-bool ChannelWin::CreatePipe(const IPC::ChannelHandle &channel_handle, |
- Mode mode) { |
- DCHECK(!pipe_.IsValid()); |
- base::string16 pipe_name; |
- // If we already have a valid pipe for channel just copy it. |
- if (channel_handle.pipe.handle) { |
- // TODO(rvargas) crbug.com/415294: ChannelHandle should either go away in |
- // favor of two independent entities (name/file), or it should be a move- |
- // only type with a base::File member. In any case, this code should not |
- // call DuplicateHandle. |
- DCHECK(channel_handle.name.empty()); |
- pipe_name = L"Not Available"; // Just used for LOG |
- // Check that the given pipe confirms to the specified mode. We can |
- // only check for PIPE_TYPE_MESSAGE & PIPE_SERVER_END flags since the |
- // other flags (PIPE_TYPE_BYTE, and PIPE_CLIENT_END) are defined as 0. |
- DWORD flags = 0; |
- GetNamedPipeInfo(channel_handle.pipe.handle, &flags, NULL, NULL, NULL); |
- DCHECK(!(flags & PIPE_TYPE_MESSAGE)); |
- if (((mode & MODE_SERVER_FLAG) && !(flags & PIPE_SERVER_END)) || |
- ((mode & MODE_CLIENT_FLAG) && (flags & PIPE_SERVER_END))) { |
- LOG(WARNING) << "Inconsistent open mode. Mode :" << mode; |
- return false; |
- } |
- HANDLE local_handle; |
- if (!DuplicateHandle(GetCurrentProcess(), |
- channel_handle.pipe.handle, |
- GetCurrentProcess(), |
- &local_handle, |
- 0, |
- FALSE, |
- DUPLICATE_SAME_ACCESS)) { |
- LOG(WARNING) << "DuplicateHandle failed. Error :" << GetLastError(); |
- return false; |
- } |
- pipe_.Set(local_handle); |
- } else if (mode & MODE_SERVER_FLAG) { |
- DCHECK(!channel_handle.pipe.handle); |
- const DWORD open_mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | |
- FILE_FLAG_FIRST_PIPE_INSTANCE; |
- pipe_name = PipeName(channel_handle.name, &client_secret_); |
- validate_client_ = !!client_secret_; |
- pipe_.Set(CreateNamedPipeW(pipe_name.c_str(), |
- open_mode, |
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, |
- 1, |
- Channel::kReadBufferSize, |
- Channel::kReadBufferSize, |
- 5000, |
- NULL)); |
- } else if (mode & MODE_CLIENT_FLAG) { |
- DCHECK(!channel_handle.pipe.handle); |
- pipe_name = PipeName(channel_handle.name, &client_secret_); |
- pipe_.Set(CreateFileW(pipe_name.c_str(), |
- GENERIC_READ | GENERIC_WRITE, |
- 0, |
- NULL, |
- OPEN_EXISTING, |
- SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | |
- FILE_FLAG_OVERLAPPED, |
- NULL)); |
- } else { |
- NOTREACHED(); |
- } |
- |
- if (!pipe_.IsValid()) { |
- // If this process is being closed, the pipe may be gone already. |
- PLOG(WARNING) << "Unable to create pipe \"" << pipe_name << "\" in " |
- << (mode & MODE_SERVER_FLAG ? "server" : "client") << " mode"; |
- return false; |
- } |
- |
- // Create the Hello message to be sent when Connect is called |
- std::unique_ptr<Message> m(new Message(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE, |
- IPC::Message::PRIORITY_NORMAL)); |
- |
- // Don't send the secret to the untrusted process, and don't send a secret |
- // if the value is zero (for IPC backwards compatability). |
- int32_t secret = validate_client_ ? 0 : client_secret_; |
- if (!m->WriteInt(GetCurrentProcessId()) || |
- (secret && !m->WriteUInt32(secret))) { |
- pipe_.Close(); |
- return false; |
- } |
- |
- OutputElement* element = new OutputElement(m.release()); |
- output_queue_.push(element); |
- return true; |
-} |
- |
-bool ChannelWin::Connect() { |
- DCHECK(base::MessageLoopForIO::IsCurrent()); |
- |
- WillConnect(); |
- |
- DLOG_IF(WARNING, thread_check_.get()) << "Connect called more than once"; |
- |
- if (!thread_check_.get()) |
- thread_check_.reset(new base::ThreadChecker()); |
- |
- if (!pipe_.IsValid()) |
- return false; |
- |
- base::MessageLoopForIO::current()->RegisterIOHandler(pipe_.Get(), this); |
- |
- // Check to see if there is a client connected to our pipe... |
- if (waiting_connect_) |
- ProcessConnection(); |
- |
- if (!input_state_.is_pending) { |
- // Complete setup asynchronously. By not setting input_state_.is_pending |
- // to true, we indicate to OnIOCompleted that this is the special |
- // initialization signal. |
- base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, |
- base::Bind(&ChannelWin::OnIOCompleted, weak_factory_.GetWeakPtr(), |
- &input_state_.context, 0, 0)); |
- } |
- |
- if (!waiting_connect_) |
- ProcessOutgoingMessages(NULL, 0); |
- return true; |
-} |
- |
-bool ChannelWin::ProcessConnection() { |
- DCHECK(thread_check_->CalledOnValidThread()); |
- if (input_state_.is_pending) |
- input_state_.is_pending = false; |
- |
- // Do we have a client connected to our pipe? |
- if (!pipe_.IsValid()) |
- return false; |
- |
- BOOL ok = ConnectNamedPipe(pipe_.Get(), &input_state_.context.overlapped); |
- DWORD err = GetLastError(); |
- if (ok) { |
- // Uhm, the API documentation says that this function should never |
- // return success when used in overlapped mode. |
- NOTREACHED(); |
- return false; |
- } |
- |
- switch (err) { |
- case ERROR_IO_PENDING: |
- input_state_.is_pending = true; |
- break; |
- case ERROR_PIPE_CONNECTED: |
- waiting_connect_ = false; |
- break; |
- case ERROR_NO_DATA: |
- // The pipe is being closed. |
- return false; |
- default: |
- NOTREACHED(); |
- return false; |
- } |
- |
- return true; |
-} |
- |
-bool ChannelWin::ProcessOutgoingMessages( |
- base::MessageLoopForIO::IOContext* context, |
- DWORD bytes_written) { |
- DCHECK(!waiting_connect_); // Why are we trying to send messages if there's |
- // no connection? |
- DCHECK(thread_check_->CalledOnValidThread()); |
- |
- if (output_state_.is_pending) { |
- DCHECK(context); |
- output_state_.is_pending = false; |
- if (!context || bytes_written == 0) { |
- DWORD err = GetLastError(); |
- LOG(ERROR) << "pipe error: " << err; |
- return false; |
- } |
- // Message was sent. |
- CHECK(!output_queue_.empty()); |
- OutputElement* element = output_queue_.front(); |
- output_queue_.pop(); |
- delete element; |
- } |
- |
- if (output_queue_.empty()) |
- return true; |
- |
- if (!pipe_.IsValid()) |
- return false; |
- |
- // Write to pipe... |
- OutputElement* element = output_queue_.front(); |
- DCHECK(element->size() <= INT_MAX); |
- BOOL ok = WriteFile(pipe_.Get(), |
- element->data(), |
- static_cast<uint32_t>(element->size()), |
- NULL, |
- &output_state_.context.overlapped); |
- if (!ok) { |
- DWORD write_error = GetLastError(); |
- if (write_error == ERROR_IO_PENDING) { |
- output_state_.is_pending = true; |
- |
- const Message* m = element->get_message(); |
- if (m) { |
- DVLOG(2) << "sent pending message @" << m << " on channel @" << this |
- << " with type " << m->type(); |
- } |
- |
- return true; |
- } |
- LOG(ERROR) << "pipe error: " << write_error; |
- return false; |
- } |
- |
- const Message* m = element->get_message(); |
- if (m) { |
- DVLOG(2) << "sent message @" << m << " on channel @" << this |
- << " with type " << m->type(); |
- } |
- |
- output_state_.is_pending = true; |
- return true; |
-} |
- |
-void ChannelWin::OnIOCompleted( |
- base::MessageLoopForIO::IOContext* context, |
- DWORD bytes_transfered, |
- DWORD error) { |
- bool ok = true; |
- DCHECK(thread_check_->CalledOnValidThread()); |
- if (context == &input_state_.context) { |
- if (waiting_connect_) { |
- if (!ProcessConnection()) |
- return; |
- // We may have some messages queued up to send... |
- if (!output_queue_.empty() && !output_state_.is_pending) |
- ProcessOutgoingMessages(NULL, 0); |
- if (input_state_.is_pending) |
- return; |
- // else, fall-through and look for incoming messages... |
- } |
- |
- // We don't support recursion through OnMessageReceived yet! |
- DCHECK(!processing_incoming_); |
- base::AutoReset<bool> auto_reset_processing_incoming( |
- &processing_incoming_, true); |
- |
- // Process the new data. |
- if (input_state_.is_pending) { |
- // This is the normal case for everything except the initialization step. |
- input_state_.is_pending = false; |
- if (!bytes_transfered) { |
- ok = false; |
- } else if (pipe_.IsValid()) { |
- ok = (AsyncReadComplete(bytes_transfered) != DISPATCH_ERROR); |
- } |
- } else { |
- DCHECK(!bytes_transfered); |
- } |
- |
- // Request more data. |
- if (ok) |
- ok = (ProcessIncomingMessages() != DISPATCH_ERROR); |
- } else { |
- DCHECK(context == &output_state_.context); |
- CHECK(output_state_.is_pending); |
- ok = ProcessOutgoingMessages(context, bytes_transfered); |
- } |
- if (!ok && pipe_.IsValid()) { |
- // We don't want to re-enter Close(). |
- Close(); |
- listener()->OnChannelError(); |
- } |
-} |
- |
-//------------------------------------------------------------------------------ |
-// Channel's methods |
- |
-// static |
-std::unique_ptr<Channel> Channel::Create( |
- const IPC::ChannelHandle& channel_handle, |
- Mode mode, |
- Listener* listener) { |
- return base::WrapUnique(new ChannelWin(channel_handle, mode, listener)); |
-} |
- |
-// static |
-bool Channel::IsNamedServerInitialized(const std::string& channel_id) { |
- return ChannelWin::IsNamedServerInitialized(channel_id); |
-} |
- |
-// static |
-std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) { |
- // Windows pipes can be enumerated by low-privileged processes. So, we |
- // append a strong random value after the \ character. This value is not |
- // included in the pipe name, but sent as part of the client hello, to |
- // hijacking the pipe name to spoof the client. |
- |
- std::string id = prefix; |
- if (!id.empty()) |
- id.append("."); |
- |
- int secret; |
- do { // Guarantee we get a non-zero value. |
- secret = base::RandInt(0, std::numeric_limits<int>::max()); |
- } while (secret == 0); |
- |
- id.append(GenerateUniqueRandomChannelID()); |
- return id.append(base::StringPrintf("\\%d", secret)); |
-} |
- |
-} // namespace IPC |