summaryrefslogtreecommitdiffstats
path: root/ipc/chromium/src/chrome
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--ipc/chromium/src/chrome/common/child_process.cc29
-rw-r--r--ipc/chromium/src/chrome/common/child_process.h45
-rw-r--r--ipc/chromium/src/chrome/common/child_process_host.cc65
-rw-r--r--ipc/chromium/src/chrome/common/child_process_host.h83
-rw-r--r--ipc/chromium/src/chrome/common/child_thread.cc44
-rw-r--r--ipc/chromium/src/chrome/common/child_thread.h56
-rw-r--r--ipc/chromium/src/chrome/common/chrome_switches.cc18
-rw-r--r--ipc/chromium/src/chrome/common/chrome_switches.h22
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel.h221
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_capability.h61
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_posix.cc1317
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_posix.h227
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_utils.cc58
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_utils.h21
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_win.cc920
-rw-r--r--ipc/chromium/src/chrome/common/ipc_channel_win.h202
-rw-r--r--ipc/chromium/src/chrome/common/ipc_message.cc218
-rw-r--r--ipc/chromium/src/chrome/common/ipc_message.h424
-rw-r--r--ipc/chromium/src/chrome/common/ipc_message_utils.cc105
-rw-r--r--ipc/chromium/src/chrome/common/ipc_message_utils.h1093
-rw-r--r--ipc/chromium/src/chrome/common/mach_ipc_mac.cc93
-rw-r--r--ipc/chromium/src/chrome/common/mach_ipc_mac.h40
-rw-r--r--ipc/chromium/src/chrome/common/mach_message_source_mac.cc61
-rw-r--r--ipc/chromium/src/chrome/common/mach_message_source_mac.h60
-rw-r--r--ipc/chromium/src/chrome/common/process_watcher.h38
-rw-r--r--ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc257
-rw-r--r--ipc/chromium/src/chrome/common/process_watcher_win.cc252
27 files changed, 6030 insertions, 0 deletions
diff --git a/ipc/chromium/src/chrome/common/child_process.cc b/ipc/chromium/src/chrome/common/child_process.cc
new file mode 100644
index 0000000000..637564c30c
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process.cc
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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 "chrome/common/child_process.h"
+
+#include "base/basictypes.h"
+#include "base/string_util.h"
+#include "chrome/common/child_thread.h"
+
+ChildProcess* ChildProcess::child_process_;
+
+ChildProcess::ChildProcess(ChildThread* child_thread)
+ : child_thread_(child_thread) {
+ DCHECK(!child_process_);
+ child_process_ = this;
+ if (child_thread_.get()) // null in unittests.
+ child_thread_->Run();
+}
+
+ChildProcess::~ChildProcess() {
+ DCHECK(child_process_ == this);
+
+ if (child_thread_.get()) child_thread_->Stop();
+
+ child_process_ = NULL;
+}
diff --git a/ipc/chromium/src/chrome/common/child_process.h b/ipc/chromium/src/chrome/common/child_process.h
new file mode 100644
index 0000000000..7ce4cffa81
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_CHILD_PROCESS_H__
+#define CHROME_COMMON_CHILD_PROCESS_H__
+
+#include <string>
+#include <vector>
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+#include "mozilla/UniquePtr.h"
+
+class ChildThread;
+
+// Base class for child processes of the browser process (i.e. renderer and
+// plugin host). This is a singleton object for each child process.
+class ChildProcess {
+ public:
+ // Child processes should have an object that derives from this class. The
+ // constructor will return once ChildThread has started.
+ explicit ChildProcess(ChildThread* child_thread);
+ virtual ~ChildProcess();
+
+ // Getter for this process' main thread.
+ ChildThread* child_thread() { return child_thread_.get(); }
+
+ // Getter for the one ChildProcess object for this process.
+ static ChildProcess* current() { return child_process_; }
+
+ private:
+ // NOTE: make sure that child_thread_ is listed before shutdown_event_, since
+ // it depends on it (indirectly through IPC::SyncChannel).
+ mozilla::UniquePtr<ChildThread> child_thread_;
+
+ // The singleton instance for this process.
+ static ChildProcess* child_process_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ChildProcess);
+};
+
+#endif // CHROME_COMMON_CHILD_PROCESS_H__
diff --git a/ipc/chromium/src/chrome/common/child_process_host.cc b/ipc/chromium/src/chrome/common/child_process_host.cc
new file mode 100644
index 0000000000..1fdb6b6e66
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process_host.cc
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2009 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 "chrome/common/child_process_host.h"
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/waitable_event.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+typedef mozilla::ipc::BrowserProcessSubThread ChromeThread;
+#include "chrome/common/process_watcher.h"
+
+using mozilla::ipc::FileDescriptor;
+
+ChildProcessHost::ChildProcessHost()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)),
+ opening_channel_(false) {}
+
+ChildProcessHost::~ChildProcessHost() {}
+
+bool ChildProcessHost::CreateChannel() {
+ channel_id_ = IPC::Channel::GenerateVerifiedChannelID();
+ channel_.reset(
+ new IPC::Channel(channel_id_, IPC::Channel::MODE_SERVER, &listener_));
+#if defined(OS_WIN)
+ channel_->StartAcceptingHandles(IPC::Channel::MODE_SERVER);
+#elif defined(OS_MACOSX)
+ channel_->StartAcceptingMachPorts(IPC::Channel::MODE_SERVER);
+#endif
+ if (!channel_->Connect()) return false;
+
+ opening_channel_ = true;
+
+ return true;
+}
+
+ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host)
+ : host_(host) {}
+
+void ChildProcessHost::ListenerHook::OnMessageReceived(
+ mozilla::UniquePtr<IPC::Message> msg) {
+ host_->OnMessageReceived(std::move(msg));
+}
+
+void ChildProcessHost::ListenerHook::OnChannelConnected(
+ base::ProcessId peer_pid) {
+ host_->opening_channel_ = false;
+ host_->OnChannelConnected(peer_pid);
+}
+
+void ChildProcessHost::ListenerHook::OnChannelError() {
+ host_->opening_channel_ = false;
+ host_->OnChannelError();
+}
+
+void ChildProcessHost::ListenerHook::GetQueuedMessages(
+ std::queue<mozilla::UniquePtr<IPC::Message>>& queue) {
+ host_->GetQueuedMessages(queue);
+}
diff --git a/ipc/chromium/src/chrome/common/child_process_host.h b/ipc/chromium/src/chrome/common/child_process_host.h
new file mode 100644
index 0000000000..195aa08a52
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_process_host.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_COMMON_CHILD_PROCESS_HOST_H_
+#define CHROME_COMMON_CHILD_PROCESS_HOST_H_
+
+#include "build/build_config.h"
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "chrome/common/ipc_channel.h"
+#include "mozilla/UniquePtr.h"
+
+namespace mozilla {
+namespace ipc {
+class FileDescriptor;
+}
+} // namespace mozilla
+
+// Plugins/workers and other child processes that live on the IO thread should
+// derive from this class.
+class ChildProcessHost : public IPC::Channel::Listener {
+ public:
+ virtual ~ChildProcessHost();
+
+ using ChannelId = IPC::Channel::ChannelId;
+
+ protected:
+ explicit ChildProcessHost();
+
+ // Derived classes return true if it's ok to shut down the child process.
+ virtual bool CanShutdown() = 0;
+
+ // Creates the IPC channel. Returns true iff it succeeded.
+ bool CreateChannel();
+
+ // IPC::Channel::Listener implementation:
+ virtual void OnMessageReceived(
+ mozilla::UniquePtr<IPC::Message> msg) override {}
+ virtual void OnChannelConnected(base::ProcessId peer_pid) override {}
+ virtual void OnChannelError() override {}
+
+ bool opening_channel() { return opening_channel_; }
+ const ChannelId& channel_id() { return channel_id_; }
+
+ IPC::Channel* channelp() const { return channel_.get(); }
+ mozilla::UniquePtr<IPC::Channel> TakeChannel() { return std::move(channel_); }
+
+ private:
+ // By using an internal class as the IPC::Channel::Listener, we can intercept
+ // OnMessageReceived/OnChannelConnected and do our own processing before
+ // calling the subclass' implementation.
+ class ListenerHook : public IPC::Channel::Listener {
+ public:
+ explicit ListenerHook(ChildProcessHost* host);
+ virtual void OnMessageReceived(
+ mozilla::UniquePtr<IPC::Message> msg) override;
+ virtual void OnChannelConnected(base::ProcessId peer_pid) override;
+ virtual void OnChannelError() override;
+ virtual void GetQueuedMessages(
+ std::queue<mozilla::UniquePtr<IPC::Message>>& queue) override;
+
+ private:
+ ChildProcessHost* host_;
+ };
+
+ ListenerHook listener_;
+
+ // True while we're waiting the channel to be opened.
+ bool opening_channel_;
+
+ // The IPC::Channel.
+ mozilla::UniquePtr<IPC::Channel> channel_;
+
+ // IPC Channel's id.
+ ChannelId channel_id_;
+};
+
+#endif // CHROME_COMMON_CHILD_PROCESS_HOST_H_
diff --git a/ipc/chromium/src/chrome/common/child_thread.cc b/ipc/chromium/src/chrome/common/child_thread.cc
new file mode 100644
index 0000000000..0206e92f4a
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_thread.cc
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2009 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 "chrome/common/child_thread.h"
+
+#include "chrome/common/child_process.h"
+#include "mozilla/ipc/NodeController.h"
+
+ChildThread::ChildThread(Thread::Options options)
+ : Thread("IPC I/O Child"),
+ owner_loop_(MessageLoop::current()),
+ options_(options) {
+ DCHECK(owner_loop_);
+ channel_name_ = IPC::Channel::ChannelIDForCurrentProcess();
+}
+
+ChildThread::~ChildThread() = default;
+
+bool ChildThread::Run() {
+ bool r = StartWithOptions(options_);
+ return r;
+}
+
+ChildThread* ChildThread::current() {
+ return ChildProcess::current()->child_thread();
+}
+
+void ChildThread::Init() {
+ auto channel = mozilla::MakeUnique<IPC::Channel>(
+ channel_name_, IPC::Channel::MODE_CLIENT, nullptr);
+#if defined(OS_WIN)
+ channel->StartAcceptingHandles(IPC::Channel::MODE_CLIENT);
+#elif defined(OS_MACOSX)
+ channel->StartAcceptingMachPorts(IPC::Channel::MODE_CLIENT);
+#endif
+
+ initial_port_ =
+ mozilla::ipc::NodeController::InitChildProcess(std::move(channel));
+}
+
+void ChildThread::CleanUp() { mozilla::ipc::NodeController::CleanUp(); }
diff --git a/ipc/chromium/src/chrome/common/child_thread.h b/ipc/chromium/src/chrome/common/child_thread.h
new file mode 100644
index 0000000000..1491d04019
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/child_thread.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_COMMON_CHILD_THREAD_H_
+#define CHROME_COMMON_CHILD_THREAD_H_
+
+#include "base/thread.h"
+#include "chrome/common/ipc_channel.h"
+#include "mojo/core/ports/port_ref.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/ipc/ScopedPort.h"
+
+class ResourceDispatcher;
+
+// Child processes's background thread should derive from this class.
+class ChildThread : public base::Thread {
+ public:
+ // Creates the thread.
+ explicit ChildThread(Thread::Options options);
+ virtual ~ChildThread();
+
+ mozilla::ipc::ScopedPort TakeInitialPort() {
+ return std::move(initial_port_);
+ }
+
+ protected:
+ friend class ChildProcess;
+
+ // Starts the thread.
+ bool Run();
+
+ protected:
+ // Returns the one child thread.
+ static ChildThread* current();
+
+ // Thread implementation.
+ virtual void Init() override;
+ virtual void CleanUp() override;
+
+ private:
+ // The message loop used to run tasks on the thread that started this thread.
+ MessageLoop* owner_loop_;
+
+ IPC::Channel::ChannelId channel_name_;
+
+ mozilla::ipc::ScopedPort initial_port_;
+
+ Thread::Options options_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ChildThread);
+};
+
+#endif // CHROME_COMMON_CHILD_THREAD_H_
diff --git a/ipc/chromium/src/chrome/common/chrome_switches.cc b/ipc/chromium/src/chrome/common/chrome_switches.cc
new file mode 100644
index 0000000000..185934c87d
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/chrome_switches.cc
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2009 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 "chrome/common/chrome_switches.h"
+
+namespace switches {
+
+// Can't find the switch you are looking for? try looking in
+// base/base_switches.cc instead.
+
+// The value of this switch tells the child process which
+// IPC channel the browser expects to use to communicate with it.
+const wchar_t kProcessChannelID[] = L"channel";
+
+} // namespace switches
diff --git a/ipc/chromium/src/chrome/common/chrome_switches.h b/ipc/chromium/src/chrome/common/chrome_switches.h
new file mode 100644
index 0000000000..d0a60508c0
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/chrome_switches.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2009 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.
+
+// Defines all the command-line switches used by Chrome.
+
+#ifndef CHROME_COMMON_CHROME_SWITCHES_H__
+#define CHROME_COMMON_CHROME_SWITCHES_H__
+
+#if defined(COMPILER_MSVC)
+# include <string.h>
+#endif
+
+namespace switches {
+
+extern const wchar_t kProcessChannelID[];
+
+} // namespace switches
+
+#endif // CHROME_COMMON_CHROME_SWITCHES_H__
diff --git a/ipc/chromium/src/chrome/common/ipc_channel.h b/ipc/chromium/src/chrome/common/ipc_channel.h
new file mode 100644
index 0000000000..3cd04ba3de
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -0,0 +1,221 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_H_
+#define CHROME_COMMON_IPC_CHANNEL_H_
+
+#include <cstdint>
+#include <queue>
+#include "base/basictypes.h"
+#include "base/process.h"
+#include "build/build_config.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/WeakPtr.h"
+#include "chrome/common/ipc_message.h"
+
+#ifdef OS_WIN
+# include <string>
+#endif
+
+namespace IPC {
+
+class Message;
+class MessageReader;
+class MessageWriter;
+
+//------------------------------------------------------------------------------
+
+class Channel {
+ // Security tests need access to the pipe handle.
+ friend class ChannelTest;
+
+ public:
+ // Windows channels use named objects and connect to them by name,
+ // but on Unix we use unnamed socketpairs and pass capabilities
+ // directly using SCM_RIGHTS messages. This type abstracts away
+ // that difference.
+#ifdef OS_WIN
+ typedef std::wstring ChannelId;
+#else
+ struct ChannelId {};
+#endif
+
+ // For channels which are created after initialization, handles to the pipe
+ // endpoints may be passed around directly using IPC messages.
+ using ChannelHandle = mozilla::UniqueFileHandle;
+
+ // Implemented by consumers of a Channel to receive messages.
+ //
+ // All listeners will only be called on the IO thread, and must be destroyed
+ // on the IO thread.
+ class Listener : public mozilla::SupportsWeakPtr {
+ public:
+ virtual ~Listener() = default;
+
+ // Called when a message is received.
+ virtual void OnMessageReceived(mozilla::UniquePtr<Message> message) = 0;
+
+ // Called when the channel is connected and we have received the internal
+ // Hello message from the peer.
+ virtual void OnChannelConnected(base::ProcessId peer_pid) {}
+
+ // Called when an error is detected that causes the channel to close.
+ // This method is not called when a channel is closed normally.
+ virtual void OnChannelError() {}
+
+ // If the listener has queued messages, swap them for |queue| like so
+ // swap(impl->my_queued_messages, queue);
+ virtual void GetQueuedMessages(
+ std::queue<mozilla::UniquePtr<Message>>& queue) {}
+ };
+
+ enum Mode { MODE_SERVER, MODE_CLIENT };
+
+ enum {
+
+ // The maximum message size in bytes. Attempting to receive a
+ // message of this size or bigger results in a channel error.
+ // This is larger in fuzzing builds to allow the fuzzing of passing
+ // large data structures into DOM methods without crashing.
+#ifndef FUZZING
+ kMaximumMessageSize = 256 * 1024 * 1024,
+#else
+ kMaximumMessageSize = 1792 * 1024 * 1024, // 1.75GB
+#endif
+
+ // Amount of data to read at once from the pipe.
+ kReadBufferSize = 4 * 1024,
+ };
+
+ // Initialize a Channel.
+ //
+ // |channel_id| identifies the communication Channel.
+ // |mode| specifies whether this Channel is to operate in server mode or
+ // client mode. In server mode, the Channel is responsible for setting up the
+ // IPC object, whereas in client mode, the Channel merely connects to the
+ // already established IPC object.
+ // |listener| receives a callback on the current thread for each newly
+ // received message.
+ //
+ // The Channel must be created and destroyed on the IO thread, and all
+ // methods, unless otherwise noted, are only safe to call on the I/O thread.
+ //
+ Channel(const ChannelId& channel_id, Mode mode, Listener* listener);
+
+ // Initialize a pre-created channel |pipe| as |mode|.
+ Channel(ChannelHandle pipe, Mode mode, Listener* listener);
+
+ ~Channel();
+
+ // Connect the pipe. On the server side, this will initiate
+ // waiting for connections. On the client, it attempts to
+ // connect to a pre-existing pipe. Note, calling Connect()
+ // will not block the calling thread and may complete
+ // asynchronously.
+ bool Connect();
+
+ // Close this Channel explicitly. May be called multiple times.
+ void Close();
+
+ // Modify the Channel's listener.
+ Listener* set_listener(Listener* listener);
+
+ // Send a message over the Channel to the listener on the other end.
+ //
+ // This method may be called from any thread, so long as the `Channel` is not
+ // destroyed before it returns.
+ //
+ // If you Send() a message on a Close()'d channel, we delete the message
+ // immediately.
+ bool Send(mozilla::UniquePtr<Message> message);
+
+ // The PID which this channel has been opened with. This will be
+ // `-1` until `OnChannelConnected` has been called.
+ int32_t OtherPid() const;
+
+ // IsClosed() is safe to call from any thread, but the value returned may
+ // be out of date.
+ bool IsClosed() const;
+
+#if defined(OS_POSIX)
+ // On POSIX an IPC::Channel wraps a socketpair(), this method returns the
+ // FD # for the client end of the socket and the equivalent FD# to use for
+ // mapping it into the Child process.
+ // This method may only be called on the server side of a channel.
+ void GetClientFileDescriptorMapping(int* src_fd, int* dest_fd) const;
+
+ // Close the client side of the socketpair.
+ void CloseClientFileDescriptor();
+
+# if defined(OS_MACOSX)
+ // Configure the mach task_t for the peer task.
+ void SetOtherMachTask(task_t task);
+
+ // Tell this pipe to accept mach ports. Exactly one side of the IPC connection
+ // must be set as `MODE_SERVER` and that side will be responsible for
+ // transferring the rights between processes.
+ void StartAcceptingMachPorts(Mode mode);
+# endif
+
+#elif defined(OS_WIN)
+ // Tell this pipe to accept handles. Exactly one side of the IPC connection
+ // must be set as `MODE_SERVER`, and that side will be responsible for calling
+ // `DuplicateHandle` to transfer the handle between processes.
+ void StartAcceptingHandles(Mode mode);
+#endif // defined(OS_POSIX)
+
+ // On Windows: Generates a channel ID that, if passed to the client
+ // as a shared secret, will validate the client's authenticity.
+ // Other platforms don't use channel IDs, so this returns the dummy
+ // ChannelId value.
+ static ChannelId GenerateVerifiedChannelID();
+
+ // On Windows: Retrieves the initial channel ID passed to the
+ // current process by its parent. Other platforms don't do this;
+ // the dummy ChannelId value is returned instead.
+ static ChannelId ChannelIDForCurrentProcess();
+
+#if defined(MOZ_WIDGET_ANDROID)
+ // Used to set the first IPC file descriptor in the child process on Android.
+ // See ipc_channel_posix.cc for further details on how this is used.
+ static void SetClientChannelFd(int fd);
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+ // Create a new pair of pipe endpoints which can be used to establish a
+ // native IPC::Channel connection.
+ static bool CreateRawPipe(ChannelHandle* server, ChannelHandle* client);
+
+ private:
+ // PIMPL to which all channel calls are delegated.
+ class ChannelImpl;
+ RefPtr<ChannelImpl> channel_impl_;
+
+ enum {
+#if defined(OS_MACOSX)
+ // If the channel receives a message that contains file descriptors, then
+ // it will reply back with this message, indicating that the message has
+ // been received. The sending channel can then close any descriptors that
+ // had been marked as auto_close. This works around a sendmsg() bug on BSD
+ // where the kernel can eagerly close file descriptors that are in message
+ // queues but not yet delivered.
+ RECEIVED_FDS_MESSAGE_TYPE = kuint16max - 1,
+#endif
+
+ // The Hello message is internal to the Channel class. It is sent
+ // by the peer when the channel is connected. The message contains
+ // just the process id (pid). The message has a special routing_id
+ // (MSG_ROUTING_NONE) and type (HELLO_MESSAGE_TYPE).
+ HELLO_MESSAGE_TYPE = kuint16max // Maximum value of message type
+ // (uint16_t), to avoid conflicting with
+ // normal message types, which are
+ // enumeration constants starting from 0.
+ };
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_capability.h b/ipc/chromium/src/chrome/common/ipc_channel_capability.h
new file mode 100644
index 0000000000..c23f16e577
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_capability.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_CAPABILITY_H_
+#define CHROME_COMMON_IPC_CHANNEL_CAPABILITY_H_
+
+#include "mozilla/ThreadSafety.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/EventTargetCapability.h"
+#include "nsISerialEventTarget.h"
+
+namespace IPC {
+
+// A thread-safety capability used in IPC channel implementations. Combines an
+// EventTargetCapability and a Mutex to allow using each independently, as well
+// as combined together.
+//
+// The ChannelCapability grants shared access if on the IOThread or if the send
+// mutex is held, and only allows exclusive access if both on the IO thread and
+// holding the send mutex. This is similar to a `MutexSingleWriter`, but more
+// flexible due to providing access to each sub-capability.
+class MOZ_CAPABILITY("channel cap") ChannelCapability {
+ public:
+ using Mutex = mozilla::Mutex;
+ using Thread = mozilla::EventTargetCapability<nsISerialEventTarget>;
+
+ ChannelCapability(const char* mutex_name, nsISerialEventTarget* io_thread)
+ : send_mutex_(mutex_name), io_thread_(io_thread) {}
+
+ const Thread& IOThread() const MOZ_RETURN_CAPABILITY(io_thread_) {
+ return io_thread_;
+ }
+ Mutex& SendMutex() MOZ_RETURN_CAPABILITY(send_mutex_) { return send_mutex_; }
+
+ // Note that we're on the IO thread, and thus have shared access to values
+ // guarded by the channel capability for the thread-safety analysis.
+ void NoteOnIOThread() const MOZ_REQUIRES(io_thread_)
+ MOZ_ASSERT_SHARED_CAPABILITY(this) {}
+
+ // Note that we're holding the send mutex, and thus have shared access to
+ // values guarded by the channel capability for the thread-safety analysis.
+ void NoteSendMutex() const MOZ_REQUIRES(send_mutex_)
+ MOZ_ASSERT_SHARED_CAPABILITY(this) {}
+
+ // Note that we're holding the send mutex while on the IO thread, and thus
+ // have exclusive access to values guarded by the channel capability for the
+ // thread-safety analysis.
+ void NoteExclusiveAccess() const MOZ_REQUIRES(io_thread_, send_mutex_)
+ MOZ_ASSERT_CAPABILITY(this) {}
+
+ private:
+ mozilla::Mutex send_mutex_;
+ mozilla::EventTargetCapability<nsISerialEventTarget> io_thread_;
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_CAPABILITY_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
new file mode 100644
index 0000000000..8b6fc3349a
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -0,0 +1,1317 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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 "chrome/common/ipc_channel_posix.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "mozilla/Mutex.h"
+#if defined(OS_MACOSX)
+# include <mach/message.h>
+# include <mach/port.h>
+# include "mozilla/UniquePtrExtensions.h"
+# include "chrome/common/mach_ipc_mac.h"
+#endif
+#if defined(OS_MACOSX) || defined(OS_NETBSD)
+# include <sched.h>
+#endif
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#include <string>
+#include <map>
+
+#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/string_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/ipc_channel_utils.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+
+// Use OS specific iovec array limit where it's possible.
+#if defined(IOV_MAX)
+static const size_t kMaxIOVecSize = IOV_MAX;
+#elif defined(ANDROID)
+static const size_t kMaxIOVecSize = 256;
+#else
+static const size_t kMaxIOVecSize = 16;
+#endif
+
+using namespace mozilla::ipc;
+
+namespace IPC {
+
+// IPC channels on Windows use named pipes (CreateNamedPipe()) with
+// channel ids as the pipe names. Channels on POSIX use anonymous
+// Unix domain sockets created via socketpair() as pipes. These don't
+// quite line up.
+//
+// When creating a child subprocess, the parent side of the fork
+// arranges it such that the initial control channel ends up on the
+// magic file descriptor gClientChannelFd in the child. Future
+// connections (file descriptors) can then be passed via that
+// connection via sendmsg().
+//
+// On Android, child processes are created as a service instead of
+// forking the parent process. The Android Binder service is used to
+// transport the IPC channel file descriptor to the child process.
+// So rather than re-mapping the file descriptor to a known value,
+// the received channel file descriptor is set by calling
+// SetClientChannelFd before gecko has been initialized and started
+// in the child process.
+
+//------------------------------------------------------------------------------
+namespace {
+
+// This is the file descriptor number that a client process expects to find its
+// IPC socket.
+static int gClientChannelFd =
+#if defined(MOZ_WIDGET_ANDROID)
+ // On android the fd is set at the time of child creation.
+ -1
+#else
+ 3
+#endif // defined(MOZ_WIDGET_ANDROID)
+ ;
+
+//------------------------------------------------------------------------------
+
+bool ErrorIsBrokenPipe(int err) { return err == EPIPE || err == ECONNRESET; }
+
+// Some Android ARM64 devices appear to have a bug where sendmsg
+// sometimes returns 0xFFFFFFFF, which we're assuming is a -1 that was
+// incorrectly truncated to 32-bit and then zero-extended.
+// See bug 1660826 for details.
+//
+// This is a workaround to detect that value and replace it with -1
+// (and check that there really was an error), because the largest
+// amount we'll ever write is Channel::kMaximumMessageSize (256MiB).
+//
+// The workaround is also enabled on x86_64 Android on debug builds,
+// although the bug isn't known to manifest there, so that there will
+// be some CI coverage of this code.
+
+static inline ssize_t corrected_sendmsg(int socket,
+ const struct msghdr* message,
+ int flags) {
+#if defined(ANDROID) && \
+ (defined(__aarch64__) || (defined(DEBUG) && defined(__x86_64__)))
+ static constexpr auto kBadValue = static_cast<ssize_t>(0xFFFFFFFF);
+ static_assert(kBadValue > 0);
+
+# ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ errno = 0;
+# endif
+ ssize_t bytes_written = sendmsg(socket, message, flags);
+ if (bytes_written == kBadValue) {
+ MOZ_DIAGNOSTIC_ASSERT(errno != 0);
+ bytes_written = -1;
+ }
+ MOZ_DIAGNOSTIC_ASSERT(bytes_written < kBadValue);
+ return bytes_written;
+#else
+ return sendmsg(socket, message, flags);
+#endif
+}
+
+} // namespace
+//------------------------------------------------------------------------------
+
+#if defined(MOZ_WIDGET_ANDROID)
+void Channel::SetClientChannelFd(int fd) { gClientChannelFd = fd; }
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
+ Listener* listener)
+ : chan_cap_("ChannelImpl::SendMutex",
+ MessageLoopForIO::current()->SerialEventTarget()) {
+ Init(mode, listener);
+
+ if (!CreatePipe(mode)) {
+ CHROMIUM_LOG(WARNING) << "Unable to create pipe in "
+ << (mode == MODE_SERVER ? "server" : "client")
+ << " mode error(" << strerror(errno) << ").";
+ return;
+ }
+
+ EnqueueHelloMessage();
+}
+
+Channel::ChannelImpl::ChannelImpl(ChannelHandle pipe, Mode mode,
+ Listener* listener)
+ : chan_cap_("ChannelImpl::SendMutex",
+ MessageLoopForIO::current()->SerialEventTarget()) {
+ Init(mode, listener);
+ SetPipe(pipe.release());
+
+ EnqueueHelloMessage();
+}
+
+void Channel::ChannelImpl::SetPipe(int fd) {
+ chan_cap_.NoteExclusiveAccess();
+
+ pipe_ = fd;
+ pipe_buf_len_ = 0;
+ if (fd >= 0) {
+ int buf_len;
+ socklen_t optlen = sizeof(buf_len);
+ if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buf_len, &optlen) != 0) {
+ CHROMIUM_LOG(WARNING)
+ << "Unable to determine pipe buffer size: " << strerror(errno);
+ return;
+ }
+ CHECK(optlen == sizeof(buf_len));
+ CHECK(buf_len > 0);
+ pipe_buf_len_ = static_cast<unsigned>(buf_len);
+ }
+}
+
+bool Channel::ChannelImpl::PipeBufHasSpaceAfter(size_t already_written) {
+ // If the OS didn't tell us the buffer size for some reason, then
+ // don't apply this limitation on the amount we try to write.
+ return pipe_buf_len_ == 0 ||
+ static_cast<size_t>(pipe_buf_len_) > already_written;
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+ // Verify that we fit in a "quantum-spaced" jemalloc bucket.
+ static_assert(sizeof(*this) <= 512, "Exceeded expected size class");
+
+ MOZ_RELEASE_ASSERT(kControlBufferHeaderSize >= CMSG_SPACE(0));
+ MOZ_RELEASE_ASSERT(kControlBufferSize >=
+ CMSG_SPACE(sizeof(int) * kControlBufferMaxFds));
+
+ chan_cap_.NoteExclusiveAccess();
+
+ mode_ = mode;
+ is_blocked_on_write_ = false;
+ partial_write_.reset();
+ input_buf_offset_ = 0;
+ input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
+ input_cmsg_buf_ = mozilla::MakeUnique<char[]>(kControlBufferSize);
+ SetPipe(-1);
+ client_pipe_ = -1;
+ listener_ = listener;
+ waiting_connect_ = true;
+#if defined(OS_MACOSX)
+ last_pending_fd_id_ = 0;
+ other_task_ = nullptr;
+#endif
+}
+
+bool Channel::ChannelImpl::CreatePipe(Mode mode) {
+ chan_cap_.NoteExclusiveAccess();
+
+ DCHECK(pipe_ == -1);
+
+ if (mode == MODE_SERVER) {
+ ChannelHandle server, client;
+ if (!Channel::CreateRawPipe(&server, &client)) {
+ return false;
+ }
+
+ SetPipe(server.release());
+ client_pipe_ = client.release();
+ } else {
+ static mozilla::Atomic<bool> consumed(false);
+ CHECK(!consumed.exchange(true))
+ << "child process main channel can be created only once";
+ SetPipe(gClientChannelFd);
+ }
+
+ return true;
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
+ mozilla::UniquePtr<Message> msg(
+ new Message(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE));
+ if (!msg->WriteInt(base::GetCurrentProcId())) {
+ CloseLocked();
+ return false;
+ }
+
+ OutputQueuePush(std::move(msg));
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ IOThread().AssertOnCurrentThread();
+ mozilla::MutexAutoLock lock(SendMutex());
+ return ConnectLocked();
+}
+
+bool Channel::ChannelImpl::ConnectLocked() {
+ chan_cap_.NoteExclusiveAccess();
+
+ if (pipe_ == -1) {
+ return false;
+ }
+
+#if defined(OS_MACOSX)
+ // If we're still waiting for our peer task to be provided, don't start
+ // listening yet. We'll start receiving messages once the task_t is set.
+ if (accept_mach_ports_ && privileged_ && !other_task_) {
+ MOZ_ASSERT(waiting_connect_);
+ return true;
+ }
+#endif
+
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_, true, MessageLoopForIO::WATCH_READ, &read_watcher_, this);
+ waiting_connect_ = false;
+
+ return ProcessOutgoingMessages();
+}
+
+bool Channel::ChannelImpl::ProcessIncomingMessages() {
+ chan_cap_.NoteOnIOThread();
+
+ struct msghdr msg = {0};
+ struct iovec iov;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = input_cmsg_buf_.get();
+
+ for (;;) {
+ msg.msg_controllen = kControlBufferSize;
+
+ if (pipe_ == -1) return false;
+
+ // In some cases the beginning of a message will be stored in input_buf_. We
+ // don't want to overwrite that, so we store the new data after it.
+ iov.iov_base = input_buf_.get() + input_buf_offset_;
+ iov.iov_len = Channel::kReadBufferSize - input_buf_offset_;
+
+ // Read from pipe.
+ // recvmsg() returns 0 if the connection has closed or EAGAIN if no data
+ // is waiting on the pipe.
+ ssize_t bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
+
+ if (bytes_read < 0) {
+ if (errno == EAGAIN) {
+ return true;
+ } else {
+ if (!ErrorIsBrokenPipe(errno)) {
+ CHROMIUM_LOG(ERROR)
+ << "pipe error (fd " << pipe_ << "): " << strerror(errno);
+ }
+ return false;
+ }
+ } else if (bytes_read == 0) {
+ // The pipe has closed...
+ Close();
+ return false;
+ }
+ DCHECK(bytes_read);
+
+ // a pointer to an array of |num_wire_fds| file descriptors from the read
+ const int* wire_fds = NULL;
+ unsigned num_wire_fds = 0;
+
+ // walk the list of control messages and, if we find an array of file
+ // descriptors, save a pointer to the array
+
+ // This next if statement is to work around an OSX issue where
+ // CMSG_FIRSTHDR will return non-NULL in the case that controllen == 0.
+ // Here's a test case:
+ //
+ // int main() {
+ // struct msghdr msg;
+ // msg.msg_control = &msg;
+ // msg.msg_controllen = 0;
+ // if (CMSG_FIRSTHDR(&msg))
+ // printf("Bug found!\n");
+ // }
+ if (msg.msg_controllen > 0) {
+ // On OSX, CMSG_FIRSTHDR doesn't handle the case where controllen is 0
+ // and will return a pointer into nowhere.
+ for (struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
+ DCHECK(payload_len % sizeof(int) == 0);
+ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
+ num_wire_fds = payload_len / 4;
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ CHROMIUM_LOG(ERROR)
+ << "SCM_RIGHTS message was truncated"
+ << " cmsg_len:" << cmsg->cmsg_len << " fd:" << pipe_;
+ for (unsigned i = 0; i < num_wire_fds; ++i)
+ IGNORE_EINTR(close(wire_fds[i]));
+ return false;
+ }
+ break;
+ }
+ }
+ }
+
+ // Process messages from input buffer.
+ const char* p = input_buf_.get();
+ const char* end = input_buf_.get() + input_buf_offset_ + bytes_read;
+
+ // A pointer to an array of |num_fds| file descriptors which includes any
+ // fds that have spilled over from a previous read.
+ const int* fds;
+ unsigned num_fds;
+ unsigned fds_i = 0; // the index of the first unused descriptor
+
+ if (input_overflow_fds_.empty()) {
+ fds = wire_fds;
+ num_fds = num_wire_fds;
+ } else {
+ // This code may look like a no-op in the case where
+ // num_wire_fds == 0, but in fact:
+ //
+ // 1. wire_fds will be nullptr, so passing it to memcpy is
+ // undefined behavior according to the C standard, even though
+ // the memcpy length is 0.
+ //
+ // 2. prev_size will be an out-of-bounds index for
+ // input_overflow_fds_; this is undefined behavior according to
+ // the C++ standard, even though the element only has its
+ // pointer taken and isn't accessed (and the corresponding
+ // operation on a C array would be defined).
+ //
+ // UBSan makes #1 a fatal error, and assertions in libstdc++ do
+ // the same for #2 if enabled.
+ if (num_wire_fds > 0) {
+ const size_t prev_size = input_overflow_fds_.size();
+ input_overflow_fds_.resize(prev_size + num_wire_fds);
+ memcpy(&input_overflow_fds_[prev_size], wire_fds,
+ num_wire_fds * sizeof(int));
+ }
+ fds = &input_overflow_fds_[0];
+ num_fds = input_overflow_fds_.size();
+ }
+
+ // The data for the message we're currently reading consists of any data
+ // stored in incoming_message_ followed by data in input_buf_ (followed by
+ // other messages).
+
+ // NOTE: We re-check `pipe_` after each message to make sure we weren't
+ // closed while calling `OnMessageReceived` or `OnChannelConnected`.
+ while (p < end && pipe_ != -1) {
+ // Try to figure out how big the message is. Size is 0 if we haven't read
+ // enough of the header to know the size.
+ uint32_t message_length = 0;
+ if (incoming_message_) {
+ message_length = incoming_message_->size();
+ } else {
+ message_length = Message::MessageSize(p, end);
+ }
+
+ if (!message_length) {
+ // We haven't seen the full message header.
+ MOZ_ASSERT(!incoming_message_);
+
+ // Move everything we have to the start of the buffer. We'll finish
+ // reading this message when we get more data. For now we leave it in
+ // input_buf_.
+ memmove(input_buf_.get(), p, end - p);
+ input_buf_offset_ = end - p;
+
+ break;
+ }
+
+ input_buf_offset_ = 0;
+
+ bool partial;
+ if (incoming_message_) {
+ // We already have some data for this message stored in
+ // incoming_message_. We want to append the new data there.
+ Message& m = *incoming_message_;
+
+ // How much data from this message remains to be added to
+ // incoming_message_?
+ MOZ_DIAGNOSTIC_ASSERT(message_length > m.CurrentSize());
+ uint32_t remaining = message_length - m.CurrentSize();
+
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(remaining, uint32_t(end - p));
+
+ m.InputBytes(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != remaining;
+ } else {
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(message_length, uint32_t(end - p));
+
+ incoming_message_ = mozilla::MakeUnique<Message>(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != message_length;
+ }
+
+ if (partial) {
+ break;
+ }
+
+ Message& m = *incoming_message_;
+
+ if (m.header()->num_handles) {
+ // the message has file descriptors
+ const char* error = NULL;
+ if (m.header()->num_handles > num_fds - fds_i) {
+ // the message has been completely received, but we didn't get
+ // enough file descriptors.
+ error = "Message needs unreceived descriptors";
+ }
+
+ if (m.header()->num_handles >
+ IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE) {
+ // There are too many descriptors in this message
+ error = "Message requires an excessive number of descriptors";
+ }
+
+ if (error) {
+ CHROMIUM_LOG(WARNING)
+ << error << " channel:" << this << " message-type:" << m.type()
+ << " header()->num_handles:" << m.header()->num_handles
+ << " num_fds:" << num_fds << " fds_i:" << fds_i;
+ // close the existing file descriptors so that we don't leak them
+ for (unsigned i = fds_i; i < num_fds; ++i)
+ IGNORE_EINTR(close(fds[i]));
+ input_overflow_fds_.clear();
+ // abort the connection
+ return false;
+ }
+
+#if defined(OS_MACOSX)
+ // Send a message to the other side, indicating that we are now
+ // responsible for closing the descriptor.
+ auto fdAck = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE,
+ RECEIVED_FDS_MESSAGE_TYPE);
+ DCHECK(m.fd_cookie() != 0);
+ fdAck->set_fd_cookie(m.fd_cookie());
+ {
+ mozilla::MutexAutoLock lock(SendMutex());
+ OutputQueuePush(std::move(fdAck));
+ }
+#endif
+
+ nsTArray<mozilla::UniqueFileHandle> handles(m.header()->num_handles);
+ for (unsigned end_i = fds_i + m.header()->num_handles; fds_i < end_i;
+ ++fds_i) {
+ handles.AppendElement(mozilla::UniqueFileHandle(fds[fds_i]));
+ }
+ m.SetAttachedFileHandles(std::move(handles));
+ }
+
+ // Note: We set other_pid_ below when we receive a Hello message (which
+ // has no routing ID), but we only emit a profiler marker for messages
+ // with a routing ID, so there's no conflict here.
+ AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
+ MessagePhase::TransferEnd);
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "received message on channel @" << this << " with type "
+ << m.type();
+#endif
+
+ if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == HELLO_MESSAGE_TYPE) {
+ // The Hello message contains only the process id.
+ int32_t other_pid = MessageIterator(m).NextInt();
+ SetOtherPid(other_pid);
+ listener_->OnChannelConnected(other_pid);
+#if defined(OS_MACOSX)
+ } else if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == RECEIVED_FDS_MESSAGE_TYPE) {
+ DCHECK(m.fd_cookie() != 0);
+ CloseDescriptors(m.fd_cookie());
+#endif
+ } else {
+ mozilla::LogIPCMessage::Run run(&m);
+#if defined(OS_MACOSX)
+ if (!AcceptMachPorts(m)) {
+ return false;
+ }
+#endif
+ listener_->OnMessageReceived(std::move(incoming_message_));
+ }
+
+ incoming_message_ = nullptr;
+ }
+
+ input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
+
+ // When the input data buffer is empty, the overflow fds should be too. If
+ // this is not the case, we probably have a rogue renderer which is trying
+ // to fill our descriptor table.
+ if (!incoming_message_ && input_buf_offset_ == 0 &&
+ !input_overflow_fds_.empty()) {
+ // We close these descriptors in Close()
+ return false;
+ }
+ }
+}
+
+bool Channel::ChannelImpl::ProcessOutgoingMessages() {
+ // NOTE: This method may be called on threads other than `IOThread()`.
+ chan_cap_.NoteSendMutex();
+
+ DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
+ // no connection?
+ is_blocked_on_write_ = false;
+
+ if (output_queue_.IsEmpty()) return true;
+
+ if (pipe_ == -1) return false;
+
+ // Write out all the messages we can till the write blocks or there are no
+ // more outgoing messages.
+ while (!output_queue_.IsEmpty()) {
+ Message* msg = output_queue_.FirstElement().get();
+
+ struct msghdr msgh = {0};
+
+ char cmsgBuf[kControlBufferSize];
+
+ if (partial_write_.isNothing()) {
+#if defined(OS_MACOSX)
+ if (!TransferMachPorts(*msg)) {
+ return false;
+ }
+#endif
+
+ if (msg->attached_handles_.Length() >
+ IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "Too many file descriptors!");
+ CHROMIUM_LOG(FATAL) << "Too many file descriptors!";
+ // This should not be reached.
+ return false;
+ }
+
+ msg->header()->num_handles = msg->attached_handles_.Length();
+#if defined(OS_MACOSX)
+ if (!msg->attached_handles_.IsEmpty()) {
+ msg->set_fd_cookie(++last_pending_fd_id_);
+ }
+#endif
+
+ Pickle::BufferList::IterImpl iter(msg->Buffers());
+ MOZ_DIAGNOSTIC_ASSERT(!iter.Done(), "empty message");
+ partial_write_.emplace(PartialWrite{iter, msg->attached_handles_});
+
+ AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferStart);
+ }
+
+ if (partial_write_->iter_.Done()) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "partial_write_->iter_ should not be done");
+ // report a send error to our caller, which will close the channel.
+ return false;
+ }
+
+ // How much of this message have we written so far?
+ Pickle::BufferList::IterImpl iter = partial_write_->iter_;
+ auto handles = partial_write_->handles_;
+
+ // Serialize attached file descriptors into the cmsg header. Only up to
+ // kControlBufferMaxFds can be serialized at once, so messages with more
+ // attachments must be sent over multiple `sendmsg` calls.
+ const size_t num_fds = std::min(handles.Length(), kControlBufferMaxFds);
+ size_t max_amt_to_write = iter.TotalBytesAvailable(msg->Buffers());
+ if (num_fds > 0) {
+ msgh.msg_control = cmsgBuf;
+ msgh.msg_controllen = CMSG_LEN(sizeof(int) * num_fds);
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msgh);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = msgh.msg_controllen;
+ for (size_t i = 0; i < num_fds; ++i) {
+ reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] = handles[i].get();
+ }
+
+ // Avoid writing one byte per remaining handle in excess of
+ // kControlBufferMaxFds. Each handle written will consume a minimum of 4
+ // bytes in the message (to store it's index), so we can depend on there
+ // being enough data to send every handle.
+ size_t remaining = handles.Length() - num_fds;
+ MOZ_ASSERT(max_amt_to_write > remaining,
+ "must be at least one byte in the message for each handle");
+ max_amt_to_write -= remaining;
+ }
+
+ // Store remaining segments to write into iovec.
+ //
+ // Don't add more than kMaxIOVecSize iovecs so that we avoid
+ // OS-dependent limits. Also, stop adding iovecs if we've already
+ // prepared to write at least the full buffer size.
+ struct iovec iov[kMaxIOVecSize];
+ size_t iov_count = 0;
+ size_t amt_to_write = 0;
+ while (!iter.Done() && iov_count < kMaxIOVecSize &&
+ PipeBufHasSpaceAfter(amt_to_write) &&
+ amt_to_write < max_amt_to_write) {
+ char* data = iter.Data();
+ size_t size =
+ std::min(iter.RemainingInSegment(), max_amt_to_write - amt_to_write);
+
+ iov[iov_count].iov_base = data;
+ iov[iov_count].iov_len = size;
+ iov_count++;
+ amt_to_write += size;
+ iter.Advance(msg->Buffers(), size);
+ }
+ MOZ_ASSERT(amt_to_write <= max_amt_to_write);
+ MOZ_ASSERT(amt_to_write > 0);
+
+ const bool intentional_short_write = !iter.Done();
+ msgh.msg_iov = iov;
+ msgh.msg_iovlen = iov_count;
+
+ ssize_t bytes_written =
+ HANDLE_EINTR(corrected_sendmsg(pipe_, &msgh, MSG_DONTWAIT));
+
+ if (bytes_written < 0) {
+ switch (errno) {
+ case EAGAIN:
+ // Not an error; the sendmsg would have blocked, so return to the
+ // event loop and try again later.
+ break;
+#if defined(OS_MACOSX) || defined(OS_NETBSD)
+ // (Note: this comment is copied from https://crrev.com/86c3d9ef4fdf6;
+ // see also bug 1142693 comment #73.)
+ //
+ // On OS X if sendmsg() is trying to send fds between processes and
+ // there isn't enough room in the output buffer to send the fd
+ // structure over atomically then EMSGSIZE is returned. The same
+ // applies to NetBSD as well.
+ //
+ // EMSGSIZE presents a problem since the system APIs can only call us
+ // when there's room in the socket buffer and not when there is
+ // "enough" room.
+ //
+ // The current behavior is to return to the event loop when EMSGSIZE
+ // is received and hopefull service another FD. This is however still
+ // technically a busy wait since the event loop will call us right
+ // back until the receiver has read enough data to allow passing the
+ // FD over atomically.
+ case EMSGSIZE:
+ // Because this is likely to result in a busy-wait, we'll try to make
+ // it easier for the receiver to make progress, but only if we're on
+ // the I/O thread already.
+ if (IOThread().IsOnCurrentThread()) {
+ sched_yield();
+ }
+ break;
+#endif
+ default:
+ if (!ErrorIsBrokenPipe(errno)) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << strerror(errno);
+ }
+ return false;
+ }
+ }
+
+ if (intentional_short_write ||
+ static_cast<size_t>(bytes_written) != amt_to_write) {
+ // If write() fails with EAGAIN or EMSGSIZE then bytes_written will be -1.
+ if (bytes_written > 0) {
+ MOZ_DIAGNOSTIC_ASSERT(intentional_short_write ||
+ static_cast<size_t>(bytes_written) <
+ amt_to_write);
+ partial_write_->iter_.AdvanceAcrossSegments(msg->Buffers(),
+ bytes_written);
+ partial_write_->handles_ = handles.From(num_fds);
+ // We should not hit the end of the buffer.
+ MOZ_DIAGNOSTIC_ASSERT(!partial_write_->iter_.Done());
+ }
+
+ is_blocked_on_write_ = true;
+ if (IOThread().IsOnCurrentThread()) {
+ // If we're on the I/O thread already, tell libevent to call us back
+ // when things are unblocked.
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_,
+ false, // One shot
+ MessageLoopForIO::WATCH_WRITE, &write_watcher_, this);
+ } else {
+ // Otherwise, emulate being called back from libevent on the I/O thread,
+ // which will re-try the write, and then potentially start watching if
+ // still necessary.
+ IOThread().Dispatch(mozilla::NewRunnableMethod<int>(
+ "ChannelImpl::ContinueProcessOutgoing", this,
+ &ChannelImpl::OnFileCanWriteWithoutBlocking, -1));
+ }
+ return true;
+ } else {
+ MOZ_ASSERT(partial_write_->handles_.Length() == num_fds,
+ "not all handles were sent");
+ partial_write_.reset();
+
+#if defined(OS_MACOSX)
+ if (!msg->attached_handles_.IsEmpty()) {
+ pending_fds_.push_back(PendingDescriptors{
+ msg->fd_cookie(), std::move(msg->attached_handles_)});
+ }
+#else
+ if (bytes_written > 0) {
+ msg->attached_handles_.Clear();
+ }
+#endif
+
+ // Message sent OK!
+
+ AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferEnd);
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent message @" << msg << " on channel @" << this
+ << " with type " << msg->type();
+#endif
+ OutputQueuePop();
+ // msg has been destroyed, so clear the dangling reference.
+ msg = nullptr;
+ }
+ }
+ return true;
+}
+
+bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
+ // NOTE: This method may be called on threads other than `IOThread()`.
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteSendMutex();
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
+ << " with type " << message->type() << " ("
+ << output_queue_.Count() << " in queue)";
+#endif
+
+ // If the channel has been closed, ProcessOutgoingMessages() is never going
+ // to pop anything off output_queue; output_queue will only get emptied when
+ // the channel is destructed. We might as well delete message now, instead
+ // of waiting for the channel to be destructed.
+ if (pipe_ == -1) {
+ if (mozilla::ipc::LoggingEnabled()) {
+ fprintf(stderr,
+ "Can't send message %s, because this channel is closed.\n",
+ message->name());
+ }
+ return false;
+ }
+
+ OutputQueuePush(std::move(message));
+ if (!waiting_connect_) {
+ if (!is_blocked_on_write_) {
+ if (!ProcessOutgoingMessages()) return false;
+ }
+ }
+
+ return true;
+}
+
+void Channel::ChannelImpl::GetClientFileDescriptorMapping(int* src_fd,
+ int* dest_fd) const {
+ IOThread().AssertOnCurrentThread();
+ DCHECK(mode_ == MODE_SERVER);
+ *src_fd = client_pipe_;
+ *dest_fd = gClientChannelFd;
+}
+
+void Channel::ChannelImpl::CloseClientFileDescriptor() {
+ IOThread().AssertOnCurrentThread();
+ if (client_pipe_ != -1) {
+ IGNORE_EINTR(close(client_pipe_));
+ client_pipe_ = -1;
+ }
+}
+
+// Called by libevent when we can read from th pipe without blocking.
+void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
+ IOThread().AssertOnCurrentThread();
+ chan_cap_.NoteOnIOThread();
+
+ if (!waiting_connect_ && fd == pipe_ && pipe_ != -1) {
+ if (!ProcessIncomingMessages()) {
+ Close();
+ listener_->OnChannelError();
+ // The OnChannelError() call may delete this, so we need to exit now.
+ return;
+ }
+ }
+}
+
+#if defined(OS_MACOSX)
+void Channel::ChannelImpl::CloseDescriptors(uint32_t pending_fd_id) {
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+
+ DCHECK(pending_fd_id != 0);
+ for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin();
+ i != pending_fds_.end(); i++) {
+ if ((*i).id == pending_fd_id) {
+ pending_fds_.erase(i);
+ return;
+ }
+ }
+ DCHECK(false) << "pending_fd_id not in our list!";
+}
+#endif
+
+void Channel::ChannelImpl::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
+ chan_cap_.NoteSendMutex();
+
+ mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
+
+ MOZ_DIAGNOSTIC_ASSERT(pipe_ != -1);
+ msg->AssertAsLargeAsHeader();
+ output_queue_.Push(std::move(msg));
+}
+
+void Channel::ChannelImpl::OutputQueuePop() {
+ // Clear any reference to the front of output_queue_ before we destroy it.
+ partial_write_.reset();
+
+ mozilla::UniquePtr<Message> message = output_queue_.Pop();
+}
+
+// Called by libevent when we can write to the pipe without blocking.
+void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) {
+ RefPtr<ChannelImpl> grip(this);
+ IOThread().AssertOnCurrentThread();
+ mozilla::ReleasableMutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+ if (pipe_ != -1 && !ProcessOutgoingMessages()) {
+ CloseLocked();
+ lock.Unlock();
+ listener_->OnChannelError();
+ }
+}
+
+void Channel::ChannelImpl::Close() {
+ IOThread().AssertOnCurrentThread();
+ mozilla::MutexAutoLock lock(SendMutex());
+ CloseLocked();
+}
+
+void Channel::ChannelImpl::CloseLocked() {
+ chan_cap_.NoteExclusiveAccess();
+
+ // Close can be called multiple times, so we need to make sure we're
+ // idempotent.
+
+ // Unregister libevent for the FIFO and close it.
+ read_watcher_.StopWatchingFileDescriptor();
+ write_watcher_.StopWatchingFileDescriptor();
+ if (pipe_ != -1) {
+ IGNORE_EINTR(close(pipe_));
+ SetPipe(-1);
+ }
+ if (client_pipe_ != -1) {
+ IGNORE_EINTR(close(client_pipe_));
+ client_pipe_ = -1;
+ }
+
+ while (!output_queue_.IsEmpty()) {
+ OutputQueuePop();
+ }
+
+ // Close any outstanding, received file descriptors
+ for (std::vector<int>::iterator i = input_overflow_fds_.begin();
+ i != input_overflow_fds_.end(); ++i) {
+ IGNORE_EINTR(close(*i));
+ }
+ input_overflow_fds_.clear();
+
+#if defined(OS_MACOSX)
+ pending_fds_.clear();
+
+ other_task_ = nullptr;
+#endif
+}
+
+#if defined(OS_MACOSX)
+void Channel::ChannelImpl::SetOtherMachTask(task_t task) {
+ IOThread().AssertOnCurrentThread();
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+
+ if (NS_WARN_IF(pipe_ == -1)) {
+ return;
+ }
+
+ MOZ_ASSERT(accept_mach_ports_ && privileged_ && waiting_connect_);
+ other_task_ = mozilla::RetainMachSendRight(task);
+ // Now that `other_task_` is provided, we can continue connecting.
+ ConnectLocked();
+}
+
+void Channel::ChannelImpl::StartAcceptingMachPorts(Mode mode) {
+ IOThread().AssertOnCurrentThread();
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+
+ if (accept_mach_ports_) {
+ MOZ_ASSERT(privileged_ == (MODE_SERVER == mode));
+ return;
+ }
+ accept_mach_ports_ = true;
+ privileged_ = MODE_SERVER == mode;
+}
+
+//------------------------------------------------------------------------------
+// Mach port transferring logic
+//
+// It is currently not possible to directly transfer a mach send right between
+// two content processes using SCM_RIGHTS, unlike how we can handle file
+// descriptors. This means that mach ports need to be transferred through a
+// separate mechanism. This file only implements support for transferring mach
+// ports between a (potentially sandboxed) child process and the parent process.
+// Support for transferring mach ports between other process pairs is handled by
+// `NodeController`, which is responsible for relaying messages which carry
+// handles via the parent process.
+//
+// The logic which we use for doing this is based on the following from
+// Chromium, which pioneered this technique. As of this writing, chromium no
+// longer uses this strategy, as all IPC messages are sent using mach ports on
+// macOS.
+// https://source.chromium.org/chromium/chromium/src/+/9f707e5e04598d8303fa99ca29eb507c839767d8:mojo/core/mach_port_relay.cc
+// https://source.chromium.org/chromium/chromium/src/+/9f707e5e04598d8303fa99ca29eb507c839767d8:base/mac/mach_port_util.cc.
+//
+// As we only need to consider messages between the privileged (parent) and
+// unprivileged (child) processes in this code, there are 2 relevant cases which
+// we need to handle:
+//
+// # Unprivileged (child) to Privileged (parent)
+//
+// As the privileged process has access to the unprivileged process' `task_t`,
+// it is possible to directly extract the mach port from the target process'
+// address space, given its name, using `mach_port_extract_right`.
+//
+// To transfer the port, the unprivileged process will leak a reference to the
+// send right, and include the port's name in the message footer. The privileged
+// process will extract that port right (and drop the reference in the old
+// process) using `mach_port_extract_right` with `MACH_MSG_TYPE_MOVE_SEND`. The
+// call to `mach_port_extract_right` is handled by `BrokerExtractSendRight`
+//
+// # Privileged (parent) to Unprivileged (child)
+//
+// Unfortunately, the process of transferring a right into a target process is
+// more complex. The only well-supported way to transfer a right into a process
+// is by sending it with `mach_msg`, and receiving it on the other side [1].
+//
+// To work around this, the privileged process uses `mach_port_allocate` to
+// create a new receive right in the target process using its `task_t`, and
+// `mach_port_extract_right` to extract a send-once right to that port. It then
+// sends a message to the port with port we're intending to send as an
+// attachment. This is handled by `BrokerTransferSendRight`, which returns the
+// name of the newly created receive right in the target process to be sent in
+// the message footer.
+//
+// In the unprivileged process, `mach_msg` is used to receive a single message
+// from the receive right, which will have the actual port we were trying to
+// transfer as an attachment. This is handled by the `MachReceivePortSendRight`
+// function.
+//
+// [1] We cannot use `mach_port_insert_right` to transfer the right into the
+// target process, as that method requires explicitly specifying the remote
+// port's name, and we do not control the port name allocator.
+
+// Extract a send right from the given peer task. A reference to the remote
+// right will be dropped. See comment above for details.
+static mozilla::UniqueMachSendRight BrokerExtractSendRight(
+ task_t task, mach_port_name_t name) {
+ mach_port_t extractedRight = MACH_PORT_NULL;
+ mach_msg_type_name_t extractedRightType;
+ kern_return_t kr =
+ mach_port_extract_right(task, name, MACH_MSG_TYPE_MOVE_SEND,
+ &extractedRight, &extractedRightType);
+ if (kr != KERN_SUCCESS) {
+ CHROMIUM_LOG(ERROR) << "failed to extract port right from other process. "
+ << mach_error_string(kr);
+ return nullptr;
+ }
+ MOZ_ASSERT(extractedRightType == MACH_MSG_TYPE_PORT_SEND,
+ "We asked the OS for a send port");
+ return mozilla::UniqueMachSendRight(extractedRight);
+}
+
+// Transfer a send right to the given peer task. The name of a receive right in
+// the remote process will be returned if successful. The sent port can be
+// obtained from that port in the peer task using `MachReceivePortSendRight`.
+// See comment above for details.
+static mozilla::Maybe<mach_port_name_t> BrokerTransferSendRight(
+ task_t task, mozilla::UniqueMachSendRight port_to_send) {
+ mach_port_name_t endpoint;
+ kern_return_t kr =
+ mach_port_allocate(task, MACH_PORT_RIGHT_RECEIVE, &endpoint);
+ if (kr != KERN_SUCCESS) {
+ CHROMIUM_LOG(ERROR)
+ << "Unable to create receive right in TransferMachPorts. "
+ << mach_error_string(kr);
+ return mozilla::Nothing();
+ }
+
+ // Clean up the endpoint on error.
+ auto destroyEndpoint =
+ mozilla::MakeScopeExit([&] { mach_port_deallocate(task, endpoint); });
+
+ // Change its message queue limit so that it accepts one message.
+ mach_port_limits limits = {};
+ limits.mpl_qlimit = 1;
+ kr = mach_port_set_attributes(task, endpoint, MACH_PORT_LIMITS_INFO,
+ reinterpret_cast<mach_port_info_t>(&limits),
+ MACH_PORT_LIMITS_INFO_COUNT);
+ if (kr != KERN_SUCCESS) {
+ CHROMIUM_LOG(ERROR)
+ << "Unable configure receive right in TransferMachPorts. "
+ << mach_error_string(kr);
+ return mozilla::Nothing();
+ }
+
+ // Get a send right.
+ mach_port_t send_once_right;
+ mach_msg_type_name_t send_right_type;
+ kr = mach_port_extract_right(task, endpoint, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &send_once_right, &send_right_type);
+ if (kr != KERN_SUCCESS) {
+ CHROMIUM_LOG(ERROR) << "Unable extract send right in TransferMachPorts. "
+ << mach_error_string(kr);
+ return mozilla::Nothing();
+ }
+ MOZ_ASSERT(MACH_MSG_TYPE_PORT_SEND_ONCE == send_right_type);
+
+ kr = MachSendPortSendRight(send_once_right, port_to_send.get(),
+ mozilla::Some(0), MACH_MSG_TYPE_MOVE_SEND_ONCE);
+ if (kr != KERN_SUCCESS) {
+ // This right will be destroyed due to being a SEND_ONCE right if we
+ // succeed.
+ mach_port_deallocate(mach_task_self(), send_once_right);
+ CHROMIUM_LOG(ERROR) << "Unable to transfer right in TransferMachPorts. "
+ << mach_error_string(kr);
+ return mozilla::Nothing();
+ }
+
+ destroyEndpoint.release();
+ return mozilla::Some(endpoint);
+}
+
+// Process footer information attached to the message, and acquire owning
+// references to any transferred mach ports. See comment above for details.
+bool Channel::ChannelImpl::AcceptMachPorts(Message& msg) {
+ chan_cap_.NoteOnIOThread();
+
+ uint32_t num_send_rights = msg.header()->num_send_rights;
+ if (num_send_rights == 0) {
+ return true;
+ }
+
+ if (!accept_mach_ports_) {
+ CHROMIUM_LOG(ERROR) << "invalid message: " << msg.name()
+ << ". channel is not configured to accept mach ports";
+ return false;
+ }
+
+ // Read in the payload from the footer, truncating the message.
+ nsTArray<uint32_t> payload;
+ payload.AppendElements(num_send_rights);
+ if (!msg.ReadFooter(payload.Elements(), num_send_rights * sizeof(uint32_t),
+ /* truncate */ true)) {
+ CHROMIUM_LOG(ERROR) << "failed to read mach port payload from message";
+ return false;
+ }
+ msg.header()->num_send_rights = 0;
+
+ // Read in the handles themselves, transferring ownership as required.
+ nsTArray<mozilla::UniqueMachSendRight> rights(num_send_rights);
+ for (uint32_t name : payload) {
+ mozilla::UniqueMachSendRight right;
+ if (privileged_) {
+ if (!other_task_) {
+ CHROMIUM_LOG(ERROR) << "other_task_ is invalid in AcceptMachPorts";
+ return false;
+ }
+ right = BrokerExtractSendRight(other_task_.get(), name);
+ } else {
+ kern_return_t kr = MachReceivePortSendRight(
+ mozilla::UniqueMachReceiveRight(name), mozilla::Some(0), &right);
+ if (kr != KERN_SUCCESS) {
+ CHROMIUM_LOG(ERROR)
+ << "failed to receive mach send right. " << mach_error_string(kr);
+ return false;
+ }
+ }
+ if (!right) {
+ return false;
+ }
+ rights.AppendElement(std::move(right));
+ }
+
+ // We're done with the handle footer, truncate the message at that point.
+ msg.attached_send_rights_ = std::move(rights);
+ MOZ_ASSERT(msg.num_send_rights() == num_send_rights);
+ return true;
+}
+
+// Transfer ownership of any attached mach ports to the peer task, and add the
+// required information for AcceptMachPorts to the message footer. See comment
+// above for details.
+bool Channel::ChannelImpl::TransferMachPorts(Message& msg) {
+ uint32_t num_send_rights = msg.num_send_rights();
+ if (num_send_rights == 0) {
+ return true;
+ }
+
+ if (!accept_mach_ports_) {
+ CHROMIUM_LOG(ERROR) << "cannot send message: " << msg.name()
+ << ". channel is not configured to accept mach ports";
+ return false;
+ }
+
+# ifdef DEBUG
+ uint32_t rights_offset = msg.header()->payload_size;
+# endif
+
+ nsTArray<uint32_t> payload(num_send_rights);
+ for (auto& port_to_send : msg.attached_send_rights_) {
+ if (privileged_) {
+ if (!other_task_) {
+ CHROMIUM_LOG(ERROR) << "other_task_ is invalid in TransferMachPorts";
+ return false;
+ }
+ mozilla::Maybe<mach_port_name_t> endpoint =
+ BrokerTransferSendRight(other_task_.get(), std::move(port_to_send));
+ if (!endpoint) {
+ return false;
+ }
+ payload.AppendElement(*endpoint);
+ } else {
+ payload.AppendElement(port_to_send.release());
+ }
+ }
+ msg.attached_send_rights_.Clear();
+
+ msg.WriteFooter(payload.Elements(), payload.Length() * sizeof(uint32_t));
+ msg.header()->num_send_rights = num_send_rights;
+
+ MOZ_ASSERT(msg.header()->payload_size ==
+ rights_offset + (sizeof(uint32_t) * num_send_rights),
+ "Unexpected number of bytes written for send rights footer?");
+ return true;
+}
+#endif
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const ChannelId& channel_id, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::Channel(ChannelHandle pipe, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(std::move(pipe), mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::~Channel() { MOZ_COUNT_DTOR(IPC::Channel); }
+
+bool Channel::Connect() { return channel_impl_->Connect(); }
+
+void Channel::Close() { channel_impl_->Close(); }
+
+Channel::Listener* Channel::set_listener(Listener* listener) {
+ return channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(mozilla::UniquePtr<Message> message) {
+ return channel_impl_->Send(std::move(message));
+}
+
+void Channel::GetClientFileDescriptorMapping(int* src_fd, int* dest_fd) const {
+ return channel_impl_->GetClientFileDescriptorMapping(src_fd, dest_fd);
+}
+
+void Channel::CloseClientFileDescriptor() {
+ channel_impl_->CloseClientFileDescriptor();
+}
+
+int32_t Channel::OtherPid() const { return channel_impl_->OtherPid(); }
+
+bool Channel::IsClosed() const { return channel_impl_->IsClosed(); }
+
+#if defined(OS_MACOSX)
+void Channel::SetOtherMachTask(task_t task) {
+ channel_impl_->SetOtherMachTask(task);
+}
+
+void Channel::StartAcceptingMachPorts(Mode mode) {
+ channel_impl_->StartAcceptingMachPorts(mode);
+}
+#endif
+
+// static
+Channel::ChannelId Channel::GenerateVerifiedChannelID() { return {}; }
+
+// static
+Channel::ChannelId Channel::ChannelIDForCurrentProcess() { return {}; }
+
+// static
+bool Channel::CreateRawPipe(ChannelHandle* server, ChannelHandle* client) {
+ int fds[2];
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ mozilla::ipc::AnnotateCrashReportWithErrno(
+ CrashReporter::Annotation::IpcCreatePipeSocketPairErrno, errno);
+ return false;
+ }
+
+ auto configureFd = [](int fd) -> bool {
+ // Mark the endpoints as non-blocking
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+ mozilla::ipc::AnnotateCrashReportWithErrno(
+ CrashReporter::Annotation::IpcCreatePipeFcntlErrno, errno);
+ return false;
+ }
+
+ // Mark the pipes as FD_CLOEXEC
+ int flags = fcntl(fd, F_GETFD);
+ if (flags == -1) {
+ mozilla::ipc::AnnotateCrashReportWithErrno(
+ CrashReporter::Annotation::IpcCreatePipeCloExecErrno, errno);
+ return false;
+ }
+ flags |= FD_CLOEXEC;
+ if (fcntl(fd, F_SETFD, flags) == -1) {
+ mozilla::ipc::AnnotateCrashReportWithErrno(
+ CrashReporter::Annotation::IpcCreatePipeCloExecErrno, errno);
+ return false;
+ }
+ return true;
+ };
+
+ if (!configureFd(fds[0]) || !configureFd(fds[1])) {
+ IGNORE_EINTR(close(fds[0]));
+ IGNORE_EINTR(close(fds[1]));
+ return false;
+ }
+
+ server->reset(fds[0]);
+ client->reset(fds[1]);
+ return true;
+}
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_posix.h b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
new file mode 100644
index 0000000000..43670db01c
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_POSIX_H_
+#define CHROME_COMMON_IPC_CHANNEL_POSIX_H_
+
+#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_channel_capability.h"
+
+#include <sys/socket.h> // for CMSG macros
+
+#include <atomic>
+#include <string>
+#include <vector>
+#include <list>
+
+#include "base/message_loop.h"
+#include "base/task.h"
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Queue.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsISupports.h"
+
+namespace IPC {
+
+// An implementation of ChannelImpl for POSIX systems that works via
+// socketpairs. See the .cc file for an overview of the implementation.
+class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET(
+ ChannelImpl, IOThread().GetEventTarget());
+
+ using ChannelId = Channel::ChannelId;
+
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const ChannelId& channel_id, Mode mode, Listener* listener);
+ ChannelImpl(ChannelHandle pipe, Mode mode, Listener* listener);
+ bool Connect() MOZ_EXCLUDES(SendMutex());
+ void Close() MOZ_EXCLUDES(SendMutex());
+ Listener* set_listener(Listener* listener) {
+ IOThread().AssertOnCurrentThread();
+ chan_cap_.NoteOnIOThread();
+ Listener* old = listener_;
+ listener_ = listener;
+ return old;
+ }
+ // NOTE: `Send` may be called on threads other than the I/O thread.
+ bool Send(mozilla::UniquePtr<Message> message) MOZ_EXCLUDES(SendMutex());
+ void GetClientFileDescriptorMapping(int* src_fd, int* dest_fd) const;
+
+ void CloseClientFileDescriptor();
+
+ int32_t OtherPid() {
+ IOThread().AssertOnCurrentThread();
+ chan_cap_.NoteOnIOThread();
+ return other_pid_;
+ }
+
+ // See the comment in ipc_channel.h for info on IsClosed()
+ // NOTE: `IsClosed` may be called on threads other than the I/O thread.
+ bool IsClosed() MOZ_EXCLUDES(SendMutex()) {
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteSendMutex();
+ return pipe_ == -1;
+ }
+
+#if defined(OS_MACOSX)
+ void SetOtherMachTask(task_t task) MOZ_EXCLUDES(SendMutex());
+
+ void StartAcceptingMachPorts(Mode mode) MOZ_EXCLUDES(SendMutex());
+#endif
+
+ private:
+ ~ChannelImpl() { Close(); }
+
+ void Init(Mode mode, Listener* listener)
+ MOZ_REQUIRES(SendMutex(), IOThread());
+ bool CreatePipe(Mode mode) MOZ_REQUIRES(SendMutex(), IOThread());
+ void SetPipe(int fd) MOZ_REQUIRES(SendMutex(), IOThread());
+ void SetOtherPid(int other_pid) MOZ_REQUIRES(IOThread())
+ MOZ_EXCLUDES(SendMutex()) {
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+ other_pid_ = other_pid;
+ }
+ bool PipeBufHasSpaceAfter(size_t already_written)
+ MOZ_REQUIRES_SHARED(chan_cap_);
+ bool EnqueueHelloMessage() MOZ_REQUIRES(SendMutex(), IOThread());
+ bool ConnectLocked() MOZ_REQUIRES(SendMutex(), IOThread());
+ void CloseLocked() MOZ_REQUIRES(SendMutex(), IOThread());
+
+ bool ProcessIncomingMessages() MOZ_REQUIRES(IOThread());
+ bool ProcessOutgoingMessages() MOZ_REQUIRES(SendMutex());
+
+ // MessageLoopForIO::Watcher implementation.
+ virtual void OnFileCanReadWithoutBlocking(int fd) override;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) override;
+
+#if defined(OS_MACOSX)
+ void CloseDescriptors(uint32_t pending_fd_id) MOZ_REQUIRES(IOThread())
+ MOZ_EXCLUDES(SendMutex());
+
+ // Called on a Message immediately before it is sent/recieved to transfer
+ // handles to the remote process, or accept handles from the remote process.
+ bool AcceptMachPorts(Message& msg) MOZ_REQUIRES(IOThread());
+ bool TransferMachPorts(Message& msg) MOZ_REQUIRES_SHARED(chan_cap_);
+#endif
+
+ void OutputQueuePush(mozilla::UniquePtr<Message> msg)
+ MOZ_REQUIRES(SendMutex());
+ void OutputQueuePop() MOZ_REQUIRES(SendMutex());
+
+ const ChannelCapability::Thread& IOThread() const
+ MOZ_RETURN_CAPABILITY(chan_cap_.IOThread()) {
+ return chan_cap_.IOThread();
+ }
+
+ ChannelCapability::Mutex& SendMutex()
+ MOZ_RETURN_CAPABILITY(chan_cap_.SendMutex()) {
+ return chan_cap_.SendMutex();
+ }
+
+ // Compound capability of a Mutex and the IO thread.
+ ChannelCapability chan_cap_;
+
+ Mode mode_ MOZ_GUARDED_BY(IOThread());
+
+ // After accepting one client connection on our server socket we want to
+ // stop listening.
+ MessageLoopForIO::FileDescriptorWatcher read_watcher_
+ MOZ_GUARDED_BY(IOThread());
+ MessageLoopForIO::FileDescriptorWatcher write_watcher_
+ MOZ_GUARDED_BY(IOThread());
+
+ // Indicates whether we're currently blocked waiting for a write to complete.
+ bool is_blocked_on_write_ MOZ_GUARDED_BY(SendMutex()) = false;
+
+ // If sending a message blocks then we use this iterator to keep track of
+ // where in the message we are. It gets reset when the message is finished
+ // sending.
+ struct PartialWrite {
+ Pickle::BufferList::IterImpl iter_;
+ mozilla::Span<const mozilla::UniqueFileHandle> handles_;
+ };
+ mozilla::Maybe<PartialWrite> partial_write_ MOZ_GUARDED_BY(SendMutex());
+
+ int pipe_ MOZ_GUARDED_BY(chan_cap_);
+ // The client end of our socketpair().
+ int client_pipe_ MOZ_GUARDED_BY(IOThread());
+ // The SO_SNDBUF value of pipe_, or 0 if unknown.
+ unsigned pipe_buf_len_ MOZ_GUARDED_BY(chan_cap_);
+
+ Listener* listener_ MOZ_GUARDED_BY(IOThread());
+
+ // Messages to be sent are queued here.
+ mozilla::Queue<mozilla::UniquePtr<Message>, 64> output_queue_
+ MOZ_GUARDED_BY(SendMutex());
+
+ // We read from the pipe into these buffers.
+ size_t input_buf_offset_ MOZ_GUARDED_BY(IOThread());
+ mozilla::UniquePtr<char[]> input_buf_ MOZ_GUARDED_BY(IOThread());
+ mozilla::UniquePtr<char[]> input_cmsg_buf_ MOZ_GUARDED_BY(IOThread());
+
+ // The control message buffer will hold all of the file descriptors that will
+ // be read in during a single recvmsg call. Message::WriteFileDescriptor
+ // always writes one word of data for every file descriptor added to the
+ // message, and the number of file descriptors per recvmsg will not exceed
+ // kControlBufferMaxFds. This is based on the true maximum SCM_RIGHTS
+ // descriptor count, which is just over 250 on both Linux and macOS.
+ //
+ // This buffer also holds a control message header of size CMSG_SPACE(0)
+ // bytes. However, CMSG_SPACE is not a constant on Macs, so we can't use it
+ // here. Consequently, we pick a number here that is at least CMSG_SPACE(0) on
+ // all platforms. We assert at runtime, in Channel::ChannelImpl::Init, that
+ // it's big enough.
+ static constexpr size_t kControlBufferMaxFds = 200;
+ static constexpr size_t kControlBufferHeaderSize = 32;
+ static constexpr size_t kControlBufferSize =
+ kControlBufferMaxFds * sizeof(int) + kControlBufferHeaderSize;
+
+ // Large incoming messages that span multiple pipe buffers get built-up in the
+ // buffers of this message.
+ mozilla::UniquePtr<Message> incoming_message_ MOZ_GUARDED_BY(IOThread());
+ std::vector<int> input_overflow_fds_ MOZ_GUARDED_BY(IOThread());
+
+ // Will be set to `true` until `Connect()` has been called and communication
+ // is ready. For privileged connections on macOS, this will not be cleared
+ // until the peer mach port has been provided to allow transferring mach
+ // ports.
+ bool waiting_connect_ MOZ_GUARDED_BY(chan_cap_) = true;
+
+ // We keep track of the PID of the other side of this channel so that we can
+ // record this when generating logs of IPC messages.
+ int32_t other_pid_ MOZ_GUARDED_BY(chan_cap_) = -1;
+
+#if defined(OS_MACOSX)
+ struct PendingDescriptors {
+ uint32_t id;
+ nsTArray<mozilla::UniqueFileHandle> handles;
+ };
+
+ std::list<PendingDescriptors> pending_fds_ MOZ_GUARDED_BY(SendMutex());
+
+ // A generation ID for RECEIVED_FD messages.
+ uint32_t last_pending_fd_id_ MOZ_GUARDED_BY(SendMutex()) = 0;
+
+ // Whether or not to accept mach ports from a remote process, and whether this
+ // process is the privileged side of a IPC::Channel which can transfer mach
+ // ports.
+ bool accept_mach_ports_ MOZ_GUARDED_BY(chan_cap_) = false;
+ bool privileged_ MOZ_GUARDED_BY(chan_cap_) = false;
+
+ // If available, the task port for the remote process.
+ mozilla::UniqueMachSendRight other_task_ MOZ_GUARDED_BY(chan_cap_);
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_POSIX_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_utils.cc b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc
new file mode 100644
index 0000000000..7e737762f3
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_utils.cc
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "chrome/common/ipc_channel_utils.h"
+
+#include "mozilla/ProfilerMarkers.h"
+#include "chrome/common/ipc_message.h"
+#include "base/process.h"
+
+namespace IPC {
+
+void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid,
+ mozilla::ipc::MessageDirection aDirection,
+ mozilla::ipc::MessagePhase aPhase) {
+ if (aMessage.routing_id() != MSG_ROUTING_NONE &&
+ profiler_feature_active(ProfilerFeature::IPCMessages)) {
+ if (static_cast<base::ProcessId>(aOtherPid) == base::kInvalidProcessId) {
+ DLOG(WARNING) << "Unable to record IPC profile marker, other PID not set";
+ return;
+ }
+
+ if (profiler_is_locked_on_current_thread()) {
+ // One of the profiler mutexes is locked on this thread, don't record
+ // markers, because we don't want to expose profiler IPCs due to the
+ // profiler itself, and also to avoid possible re-entrancy issues.
+ return;
+ }
+
+ // The current timestamp must be given to the `IPCMarker` payload.
+ [[maybe_unused]] const mozilla::TimeStamp now = mozilla::TimeStamp::Now();
+ bool isThreadBeingProfiled =
+ profiler_thread_is_being_profiled_for_markers();
+ PROFILER_MARKER(
+ "IPC", IPC,
+ mozilla::MarkerOptions(
+ mozilla::MarkerTiming::InstantAt(now),
+ // If the thread is being profiled, add the marker to
+ // the current thread. If the thread is not being
+ // profiled, add the marker to the main thread. It will
+ // appear in the main thread's IPC track. Profiler
+ // analysis UI correlates all the IPC markers from
+ // different threads and generates processed markers.
+ isThreadBeingProfiled ? mozilla::MarkerThreadId::CurrentThread()
+ : mozilla::MarkerThreadId::MainThread()),
+ IPCMarker, now, now, aOtherPid, aMessage.seqno(), aMessage.type(),
+ mozilla::ipc::UnknownSide, aDirection, aPhase, aMessage.is_sync(),
+ // aOriginThreadId: If the thread is being profiled, do not include a
+ // thread ID, as it's the same as the markers. Only include this field
+ // when the marker is being sent from another thread.
+ isThreadBeingProfiled ? mozilla::MarkerThreadId{}
+ : mozilla::MarkerThreadId::CurrentThread());
+ }
+}
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_utils.h b/ipc/chromium/src/chrome/common/ipc_channel_utils.h
new file mode 100644
index 0000000000..1bcfcf7032
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_utils.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_UTILS_H_
+#define CHROME_COMMON_IPC_CHANNEL_UTILS_H_
+
+#include "chrome/common/ipc_message.h"
+#include "mozilla/ipc/MessageChannel.h"
+
+namespace IPC {
+
+void AddIPCProfilerMarker(const Message& aMessage, int32_t aOtherPid,
+ mozilla::ipc::MessageDirection aDirection,
+ mozilla::ipc::MessagePhase aPhase);
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_UTILS_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.cc b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
new file mode 100644
index 0000000000..10a9f2d91f
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -0,0 +1,920 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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 "chrome/common/ipc_channel_win.h"
+
+#include <windows.h>
+#include <sstream>
+
+#include "base/command_line.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/rand_util.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/ipc_channel_utils.h"
+#include "chrome/common/ipc_message_utils.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/LateWriteChecks.h"
+#include "mozilla/RandomNum.h"
+#include "nsThreadUtils.h"
+
+using namespace mozilla::ipc;
+
+namespace IPC {
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::State::State(ChannelImpl* channel) {
+ memset(&context.overlapped, 0, sizeof(context.overlapped));
+ context.handler = channel;
+}
+
+Channel::ChannelImpl::State::~State() {
+ COMPILE_ASSERT(!offsetof(Channel::ChannelImpl::State, context),
+ starts_with_io_context);
+}
+
+//------------------------------------------------------------------------------
+
+Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
+ Listener* listener)
+ : chan_cap_("ChannelImpl::SendMutex",
+ MessageLoopForIO::current()->SerialEventTarget()),
+ ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)) {
+ Init(mode, listener);
+
+ if (!CreatePipe(channel_id, mode)) {
+ // The pipe may have been closed already.
+ CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id
+ << "\" in " << (mode == 0 ? "server" : "client")
+ << " mode.";
+ }
+}
+
+Channel::ChannelImpl::ChannelImpl(ChannelHandle pipe, Mode mode,
+ Listener* listener)
+ : chan_cap_("ChannelImpl::SendMutex",
+ MessageLoopForIO::current()->SerialEventTarget()),
+ ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)) {
+ Init(mode, listener);
+
+ if (!pipe) {
+ return;
+ }
+
+ shared_secret_ = 0;
+ waiting_for_shared_secret_ = false;
+ pipe_ = pipe.release();
+ EnqueueHelloMessage();
+}
+
+void Channel::ChannelImpl::Init(Mode mode, Listener* listener) {
+ // Verify that we fit in a "quantum-spaced" jemalloc bucket.
+ static_assert(sizeof(*this) <= 512, "Exceeded expected size class");
+
+ chan_cap_.NoteExclusiveAccess();
+
+ mode_ = mode;
+ pipe_ = INVALID_HANDLE_VALUE;
+ listener_ = listener;
+ waiting_connect_ = true;
+ processing_incoming_ = false;
+ input_buf_offset_ = 0;
+ input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
+ accept_handles_ = false;
+ privileged_ = false;
+ other_process_ = INVALID_HANDLE_VALUE;
+}
+
+void Channel::ChannelImpl::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
+ chan_cap_.NoteSendMutex();
+
+ mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
+
+ output_queue_.Push(std::move(msg));
+}
+
+void Channel::ChannelImpl::OutputQueuePop() {
+ mozilla::UniquePtr<Message> message = output_queue_.Pop();
+}
+
+void Channel::ChannelImpl::Close() {
+ IOThread().AssertOnCurrentThread();
+ mozilla::MutexAutoLock lock(SendMutex());
+ CloseLocked();
+}
+
+void Channel::ChannelImpl::CloseLocked() {
+ chan_cap_.NoteExclusiveAccess();
+
+ if (connect_timeout_) {
+ connect_timeout_->Cancel();
+ connect_timeout_ = nullptr;
+ }
+
+ // If we still have pending I/O, cancel it. The references inside
+ // `input_state_` and `output_state_` will keep the buffers alive until they
+ // complete.
+ if (input_state_.is_pending || output_state_.is_pending) {
+ CancelIo(pipe_);
+ }
+
+ // Closing the handle at this point prevents us from issuing more requests
+ // form OnIOCompleted().
+ if (pipe_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(pipe_);
+ pipe_ = INVALID_HANDLE_VALUE;
+ }
+
+ // If we have a connection to the other process, close the handle.
+ if (other_process_ != INVALID_HANDLE_VALUE) {
+ CloseHandle(other_process_);
+ other_process_ = INVALID_HANDLE_VALUE;
+ }
+
+ // Don't return from `CloseLocked()` until the IO has been completed,
+ // otherwise the IO thread may exit with outstanding IO, leaking the
+ // ChannelImpl.
+ //
+ // It's OK to unlock here, as calls to `Send` from other threads will be
+ // rejected, due to `pipe_` having been cleared.
+ while (input_state_.is_pending || output_state_.is_pending) {
+ mozilla::MutexAutoUnlock unlock(SendMutex());
+ MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
+ }
+
+ while (!output_queue_.IsEmpty()) {
+ OutputQueuePop();
+ }
+}
+
+bool Channel::ChannelImpl::Send(mozilla::UniquePtr<Message> message) {
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteSendMutex();
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sending message @" << message.get() << " on channel @" << this
+ << " with type " << message->type() << " ("
+ << output_queue_.Count() << " in queue)";
+#endif
+
+ if (pipe_ == INVALID_HANDLE_VALUE) {
+ if (mozilla::ipc::LoggingEnabled()) {
+ fprintf(stderr,
+ "Can't send message %s, because this channel is closed.\n",
+ message->name());
+ }
+ return false;
+ }
+
+ OutputQueuePush(std::move(message));
+ // ensure waiting to write
+ if (!waiting_connect_) {
+ if (!output_state_.is_pending) {
+ if (!ProcessOutgoingMessages(NULL, 0, false)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+const Channel::ChannelId Channel::ChannelImpl::PipeName(
+ const ChannelId& channel_id, int32_t* secret) const {
+ MOZ_ASSERT(secret);
+
+ std::wostringstream ss;
+ ss << L"\\\\.\\pipe\\chrome.";
+
+ // Prevent the shared secret from ending up in the pipe name.
+ size_t index = channel_id.find_first_of(L'\\');
+ if (index != std::string::npos) {
+ StringToInt(channel_id.substr(index + 1), secret);
+ ss << channel_id.substr(0, index - 1);
+ } else {
+ // This case is here to support predictable named pipes in tests.
+ *secret = 0;
+ ss << channel_id;
+ }
+ return ss.str();
+}
+
+bool Channel::ChannelImpl::CreatePipe(const ChannelId& channel_id, Mode mode) {
+ chan_cap_.NoteExclusiveAccess();
+
+ DCHECK(pipe_ == INVALID_HANDLE_VALUE);
+ const ChannelId pipe_name = PipeName(channel_id, &shared_secret_);
+ if (mode == MODE_SERVER) {
+ waiting_for_shared_secret_ = !!shared_secret_;
+ pipe_ = CreateNamedPipeW(pipe_name.c_str(),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1, // number of pipe instances
+ // output buffer size (XXX tune)
+ Channel::kReadBufferSize,
+ // input buffer size (XXX tune)
+ Channel::kReadBufferSize,
+ 5000, // timeout in milliseconds (XXX tune)
+ NULL);
+ } else {
+ pipe_ = CreateFileW(
+ pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION | FILE_FLAG_OVERLAPPED,
+ NULL);
+ }
+ if (pipe_ == INVALID_HANDLE_VALUE) {
+ // If this process is being closed, the pipe may be gone already.
+ CHROMIUM_LOG(WARNING) << "failed to create pipe: " << GetLastError();
+ return false;
+ }
+
+ // Create the Hello message to be sent when Connect is called
+ return EnqueueHelloMessage();
+}
+
+bool Channel::ChannelImpl::EnqueueHelloMessage() {
+ chan_cap_.NoteExclusiveAccess();
+
+ auto m = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE, HELLO_MESSAGE_TYPE);
+
+ // If we're waiting for our shared secret from the other end's hello message
+ // then don't give the game away by sending it in ours.
+ int32_t secret = waiting_for_shared_secret_ ? 0 : shared_secret_;
+
+ // Also, don't send if the value is zero (for IPC backwards compatability).
+ if (!m->WriteInt(GetCurrentProcessId()) ||
+ (secret && !m->WriteUInt32(secret))) {
+ CloseHandle(pipe_);
+ pipe_ = INVALID_HANDLE_VALUE;
+ return false;
+ }
+
+ OutputQueuePush(std::move(m));
+ return true;
+}
+
+bool Channel::ChannelImpl::Connect() {
+ IOThread().AssertOnCurrentThread();
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+
+ if (pipe_ == INVALID_HANDLE_VALUE) return false;
+
+ MessageLoopForIO::current()->RegisterIOHandler(pipe_, this);
+
+ // Check to see if there is a client connected to our pipe...
+ if (mode_ == MODE_SERVER) {
+ DCHECK(!input_state_.is_pending);
+ if (!ProcessConnection()) {
+ return false;
+ }
+ } else {
+ waiting_connect_ = false;
+ }
+
+ if (!input_state_.is_pending) {
+ // Complete setup asynchronously. By not setting input_state_.is_pending
+ // to `this`, we indicate to OnIOCompleted that this is the special
+ // initialization signal, while keeping a reference through the
+ // `RunnableMethod`.
+ IOThread().Dispatch(
+ mozilla::NewRunnableMethod<MessageLoopForIO::IOContext*, DWORD, DWORD>(
+ "ContinueConnect", this, &ChannelImpl::OnIOCompleted,
+ &input_state_.context, 0, 0));
+ }
+
+ if (!waiting_connect_) {
+ DCHECK(!output_state_.is_pending);
+ ProcessOutgoingMessages(NULL, 0, false);
+ }
+ return true;
+}
+
+bool Channel::ChannelImpl::ProcessConnection() {
+ chan_cap_.NoteExclusiveAccess();
+
+ DCHECK(!input_state_.is_pending);
+
+ // Do we have a client connected to our pipe?
+ if (INVALID_HANDLE_VALUE == pipe_) return false;
+
+ BOOL ok = ConnectNamedPipe(pipe_, &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: {
+ static bool kExtendedTimeout =
+#ifdef DEBUG
+ true;
+#else
+ !!PR_GetEnv("MOZ_RUN_GTEST");
+#endif
+ input_state_.is_pending = this;
+ NS_NewTimerWithCallback(
+ getter_AddRefs(connect_timeout_),
+ [self = RefPtr{this}](nsITimer* timer) {
+ CHROMIUM_LOG(ERROR) << "ConnectNamedPipe timed out!";
+ self->IOThread().AssertOnCurrentThread();
+ self->Close();
+ self->listener_->OnChannelError();
+ },
+ kExtendedTimeout ? 60000 : 10000, nsITimer::TYPE_ONE_SHOT,
+ "ChannelImpl::ProcessConnection", IOThread().GetEventTarget());
+ } break;
+ case ERROR_PIPE_CONNECTED:
+ waiting_connect_ = false;
+ if (connect_timeout_) {
+ connect_timeout_->Cancel();
+ connect_timeout_ = nullptr;
+ }
+ break;
+ case ERROR_NO_DATA:
+ // The pipe is being closed.
+ return false;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ return true;
+}
+
+void Channel::ChannelImpl::SetOtherPid(int other_pid) {
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+ other_pid_ = other_pid;
+
+ // Now that we know the remote pid, open a privileged handle to the
+ // child process if needed to transfer handles to/from it.
+ if (privileged_ && other_process_ == INVALID_HANDLE_VALUE) {
+ other_process_ = OpenProcess(PROCESS_DUP_HANDLE, false, other_pid_);
+ if (!other_process_) {
+ other_process_ = INVALID_HANDLE_VALUE;
+ CHROMIUM_LOG(ERROR) << "Failed to acquire privileged handle to "
+ << other_pid_ << ", cannot accept handles";
+ }
+ }
+}
+
+bool Channel::ChannelImpl::ProcessIncomingMessages(
+ MessageLoopForIO::IOContext* context, DWORD bytes_read, bool was_pending) {
+ chan_cap_.NoteOnIOThread();
+
+ DCHECK(!input_state_.is_pending);
+
+ if (was_pending) {
+ DCHECK(context);
+
+ if (!context || !bytes_read) return false;
+ } else {
+ // This happens at channel initialization.
+ DCHECK(!bytes_read && context == &input_state_.context);
+ }
+
+ for (;;) {
+ if (bytes_read == 0) {
+ if (INVALID_HANDLE_VALUE == pipe_) return false;
+
+ // Read from pipe...
+ BOOL ok = ReadFile(pipe_, input_buf_.get() + input_buf_offset_,
+ Channel::kReadBufferSize - input_buf_offset_,
+ &bytes_read, &input_state_.context.overlapped);
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ input_state_.is_pending = this;
+ return true;
+ }
+ if (err != ERROR_BROKEN_PIPE) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << err;
+ }
+ return false;
+ }
+ input_state_.is_pending = this;
+ return true;
+ }
+ DCHECK(bytes_read);
+
+ // Process messages from input buffer.
+
+ const char* p = input_buf_.get();
+ const char* end = input_buf_.get() + input_buf_offset_ + bytes_read;
+
+ // NOTE: We re-check `pipe_` after each message to make sure we weren't
+ // closed while calling `OnMessageReceived` or `OnChannelConnected`.
+ while (p < end && INVALID_HANDLE_VALUE != pipe_) {
+ // Try to figure out how big the message is. Size is 0 if we haven't read
+ // enough of the header to know the size.
+ uint32_t message_length = 0;
+ if (incoming_message_) {
+ message_length = incoming_message_->size();
+ } else {
+ message_length = Message::MessageSize(p, end);
+ }
+
+ if (!message_length) {
+ // We haven't seen the full message header.
+ MOZ_ASSERT(!incoming_message_);
+
+ // Move everything we have to the start of the buffer. We'll finish
+ // reading this message when we get more data. For now we leave it in
+ // input_buf_.
+ memmove(input_buf_.get(), p, end - p);
+ input_buf_offset_ = end - p;
+
+ break;
+ }
+
+ input_buf_offset_ = 0;
+
+ bool partial;
+ if (incoming_message_) {
+ // We already have some data for this message stored in
+ // incoming_message_. We want to append the new data there.
+ Message& m = *incoming_message_;
+
+ // How much data from this message remains to be added to
+ // incoming_message_?
+ MOZ_ASSERT(message_length > m.CurrentSize());
+ uint32_t remaining = message_length - m.CurrentSize();
+
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(remaining, uint32_t(end - p));
+
+ m.InputBytes(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != remaining;
+ } else {
+ // How much data from this message is stored in input_buf_?
+ uint32_t in_buf = std::min(message_length, uint32_t(end - p));
+
+ incoming_message_ = mozilla::MakeUnique<Message>(p, in_buf);
+ p += in_buf;
+
+ // Are we done reading this message?
+ partial = in_buf != message_length;
+ }
+
+ if (partial) {
+ break;
+ }
+
+ Message& m = *incoming_message_;
+
+ // Note: We set other_pid_ below when we receive a Hello message (which
+ // has no routing ID), but we only emit a profiler marker for messages
+ // with a routing ID, so there's no conflict here.
+ AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
+ MessagePhase::TransferEnd);
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "received message on channel @" << this << " with type "
+ << m.type();
+#endif
+ if (m.routing_id() == MSG_ROUTING_NONE &&
+ m.type() == HELLO_MESSAGE_TYPE) {
+ // The Hello message contains the process id and must include the
+ // shared secret, if we are waiting for it.
+ MessageIterator it = MessageIterator(m);
+ int32_t other_pid = it.NextInt();
+ SetOtherPid(other_pid);
+ if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
+ NOTREACHED();
+ // Something went wrong. Abort connection.
+ // NOTE: Caller will `Close()` and notify `OnChannelError`.
+ return false;
+ }
+ waiting_for_shared_secret_ = false;
+
+ listener_->OnChannelConnected(other_pid);
+ } else {
+ mozilla::LogIPCMessage::Run run(&m);
+ if (!AcceptHandles(m)) {
+ return false;
+ }
+ listener_->OnMessageReceived(std::move(incoming_message_));
+ }
+
+ incoming_message_ = nullptr;
+ }
+
+ bytes_read = 0; // Get more data.
+ }
+}
+
+bool Channel::ChannelImpl::ProcessOutgoingMessages(
+ MessageLoopForIO::IOContext* context, DWORD bytes_written,
+ bool was_pending) {
+ chan_cap_.NoteSendMutex();
+
+ DCHECK(!output_state_.is_pending);
+ DCHECK(!waiting_connect_); // Why are we trying to send messages if there's
+ // no connection?
+ if (was_pending) {
+ DCHECK(context);
+ if (!context || bytes_written == 0) {
+ DWORD err = GetLastError();
+ if (err != ERROR_BROKEN_PIPE) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << err;
+ }
+ return false;
+ }
+ // Message was sent.
+ DCHECK(!output_queue_.IsEmpty());
+ Message* m = output_queue_.FirstElement().get();
+
+ MOZ_RELEASE_ASSERT(partial_write_iter_.isSome());
+ Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
+ iter.Advance(m->Buffers(), bytes_written);
+ if (iter.Done()) {
+ AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferEnd);
+
+ partial_write_iter_.reset();
+ OutputQueuePop();
+ // m has been destroyed, so clear the dangling reference.
+ m = nullptr;
+ }
+ }
+
+ if (output_queue_.IsEmpty()) return true;
+
+ if (INVALID_HANDLE_VALUE == pipe_) return false;
+
+ // Write to pipe...
+ Message* m = output_queue_.FirstElement().get();
+
+ if (partial_write_iter_.isNothing()) {
+ AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
+ MessagePhase::TransferStart);
+ if (!TransferHandles(*m)) {
+ return false;
+ }
+ Pickle::BufferList::IterImpl iter(m->Buffers());
+ partial_write_iter_.emplace(iter);
+ }
+
+ Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
+
+ // Don't count this write for the purposes of late write checking. If this
+ // message results in a legitimate file write, that will show up when it
+ // happens.
+ mozilla::PushSuspendLateWriteChecks();
+ BOOL ok = WriteFile(pipe_, iter.Data(), iter.RemainingInSegment(),
+ &bytes_written, &output_state_.context.overlapped);
+ mozilla::PopSuspendLateWriteChecks();
+
+ if (!ok) {
+ DWORD err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ output_state_.is_pending = this;
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent pending message @" << m << " on channel @" << this
+ << " with type " << m->type();
+#endif
+
+ return true;
+ }
+ if (err != ERROR_BROKEN_PIPE) {
+ CHROMIUM_LOG(ERROR) << "pipe error: " << err;
+ }
+ return false;
+ }
+
+#ifdef IPC_MESSAGE_DEBUG_EXTRA
+ DLOG(INFO) << "sent message @" << m << " on channel @" << this
+ << " with type " << m->type();
+#endif
+
+ output_state_.is_pending = this;
+ return true;
+}
+
+void Channel::ChannelImpl::OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error) {
+ // NOTE: In case the pending reference was the last reference, release it
+ // outside of the lock.
+ RefPtr<ChannelImpl> was_pending;
+
+ IOThread().AssertOnCurrentThread();
+ chan_cap_.NoteOnIOThread();
+
+ bool ok;
+ if (context == &input_state_.context) {
+ was_pending = input_state_.is_pending.forget();
+ bool was_waiting_connect = waiting_connect_;
+ if (was_waiting_connect) {
+ mozilla::MutexAutoLock lock(SendMutex());
+ if (!ProcessConnection()) {
+ return;
+ }
+ // We may have some messages queued up to send...
+ if (!output_queue_.IsEmpty() && !output_state_.is_pending) {
+ ProcessOutgoingMessages(NULL, 0, false);
+ }
+ 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_);
+ processing_incoming_ = true;
+ ok = ProcessIncomingMessages(context, bytes_transfered,
+ was_pending && !was_waiting_connect);
+ processing_incoming_ = false;
+ } else {
+ mozilla::MutexAutoLock lock(SendMutex());
+ DCHECK(context == &output_state_.context);
+ was_pending = output_state_.is_pending.forget();
+ ok = ProcessOutgoingMessages(context, bytes_transfered, was_pending);
+ }
+ if (!ok && INVALID_HANDLE_VALUE != pipe_) {
+ // We don't want to re-enter Close().
+ Close();
+ listener_->OnChannelError();
+ }
+}
+
+void Channel::ChannelImpl::StartAcceptingHandles(Mode mode) {
+ IOThread().AssertOnCurrentThread();
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteExclusiveAccess();
+
+ if (accept_handles_) {
+ MOZ_ASSERT(privileged_ == (mode == MODE_SERVER));
+ return;
+ }
+ accept_handles_ = true;
+ privileged_ = mode == MODE_SERVER;
+
+ if (privileged_ && other_pid_ != -1 &&
+ other_process_ == INVALID_HANDLE_VALUE) {
+ other_process_ = OpenProcess(PROCESS_DUP_HANDLE, false, other_pid_);
+ if (!other_process_) {
+ other_process_ = INVALID_HANDLE_VALUE;
+ CHROMIUM_LOG(ERROR) << "Failed to acquire privileged handle to "
+ << other_pid_ << ", cannot accept handles";
+ }
+ }
+}
+
+static uint32_t HandleToUint32(HANDLE h) {
+ // Cast through uintptr_t and then unsigned int to make the truncation to
+ // 32 bits explicit. Handles are size of-pointer but are always 32-bit values.
+ // https://docs.microsoft.com/en-ca/windows/win32/winprog64/interprocess-communication
+ // says: 64-bit versions of Windows use 32-bit handles for interoperability.
+ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h));
+}
+
+static HANDLE Uint32ToHandle(uint32_t h) {
+ return reinterpret_cast<HANDLE>(
+ static_cast<uintptr_t>(static_cast<int32_t>(h)));
+}
+
+bool Channel::ChannelImpl::AcceptHandles(Message& msg) {
+ chan_cap_.NoteOnIOThread();
+
+ MOZ_ASSERT(msg.num_handles() == 0);
+
+ uint32_t num_handles = msg.header()->num_handles;
+ if (num_handles == 0) {
+ return true;
+ }
+
+ if (!accept_handles_) {
+ CHROMIUM_LOG(ERROR) << "invalid message: " << msg.name()
+ << ". channel is not configured to accept handles";
+ return false;
+ }
+
+ // Read in the payload from the footer, truncating the message.
+ nsTArray<uint32_t> payload;
+ payload.AppendElements(num_handles);
+ if (!msg.ReadFooter(payload.Elements(), num_handles * sizeof(uint32_t),
+ /* truncate */ true)) {
+ CHROMIUM_LOG(ERROR) << "failed to read handle payload from message";
+ return false;
+ }
+ msg.header()->num_handles = 0;
+
+ // Read in the handles themselves, transferring ownership as required.
+ nsTArray<mozilla::UniqueFileHandle> handles(num_handles);
+ for (uint32_t handleValue : payload) {
+ HANDLE handle = Uint32ToHandle(handleValue);
+
+ // If we're the privileged process, the remote process will have leaked
+ // the sent handles in its local address space, and be relying on us to
+ // duplicate them, otherwise the remote privileged side will have
+ // transferred the handles to us already.
+ if (privileged_) {
+ if (other_process_ == INVALID_HANDLE_VALUE) {
+ CHROMIUM_LOG(ERROR) << "other_process_ is invalid in AcceptHandles";
+ return false;
+ }
+ if (!::DuplicateHandle(other_process_, handle, GetCurrentProcess(),
+ &handle, 0, FALSE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
+ CHROMIUM_LOG(ERROR) << "DuplicateHandle failed for handle " << handle
+ << " in AcceptHandles";
+ return false;
+ }
+ }
+
+ // The handle is directly owned by this process now, and can be added to
+ // our `handles` array.
+ handles.AppendElement(mozilla::UniqueFileHandle(handle));
+ }
+
+ // We're done with the handle footer, truncate the message at that point.
+ msg.SetAttachedFileHandles(std::move(handles));
+ MOZ_ASSERT(msg.num_handles() == num_handles);
+ return true;
+}
+
+bool Channel::ChannelImpl::TransferHandles(Message& msg) {
+ chan_cap_.NoteSendMutex();
+
+ MOZ_ASSERT(msg.header()->num_handles == 0);
+
+ uint32_t num_handles = msg.num_handles();
+ if (num_handles == 0) {
+ return true;
+ }
+
+ if (!accept_handles_) {
+ CHROMIUM_LOG(ERROR) << "cannot send message: " << msg.name()
+ << ". channel is not configured to accept handles";
+ return false;
+ }
+
+#ifdef DEBUG
+ uint32_t handles_offset = msg.header()->payload_size;
+#endif
+
+ nsTArray<uint32_t> payload(num_handles);
+ for (uint32_t i = 0; i < num_handles; ++i) {
+ // Release ownership of the handle. It'll be cloned when the parent process
+ // transfers it with DuplicateHandle either in this process or the remote
+ // process.
+ HANDLE handle = msg.attached_handles_[i].release();
+
+ // If we're the privileged process, transfer the HANDLE to our remote before
+ // sending the message. Otherwise, the remote privileged process will
+ // transfer the handle for us, so leak it.
+ if (privileged_) {
+ if (other_process_ == INVALID_HANDLE_VALUE) {
+ CHROMIUM_LOG(ERROR) << "other_process_ is invalid in TransferHandles";
+ return false;
+ }
+ if (!::DuplicateHandle(GetCurrentProcess(), handle, other_process_,
+ &handle, 0, FALSE,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
+ CHROMIUM_LOG(ERROR) << "DuplicateHandle failed for handle " << handle
+ << " in TransferHandles";
+ return false;
+ }
+ }
+
+ payload.AppendElement(HandleToUint32(handle));
+ }
+ msg.attached_handles_.Clear();
+
+ msg.WriteFooter(payload.Elements(), payload.Length() * sizeof(uint32_t));
+ msg.header()->num_handles = num_handles;
+
+ MOZ_ASSERT(msg.header()->payload_size ==
+ handles_offset + (sizeof(uint32_t) * num_handles),
+ "Unexpected number of bytes written for handles footer?");
+ return true;
+}
+
+//------------------------------------------------------------------------------
+// Channel's methods simply call through to ChannelImpl.
+Channel::Channel(const ChannelId& channel_id, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(channel_id, mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::Channel(ChannelHandle pipe, Mode mode, Listener* listener)
+ : channel_impl_(new ChannelImpl(std::move(pipe), mode, listener)) {
+ MOZ_COUNT_CTOR(IPC::Channel);
+}
+
+Channel::~Channel() { MOZ_COUNT_DTOR(IPC::Channel); }
+
+bool Channel::Connect() { return channel_impl_->Connect(); }
+
+void Channel::Close() { channel_impl_->Close(); }
+
+void Channel::StartAcceptingHandles(Mode mode) {
+ channel_impl_->StartAcceptingHandles(mode);
+}
+
+Channel::Listener* Channel::set_listener(Listener* listener) {
+ return channel_impl_->set_listener(listener);
+}
+
+bool Channel::Send(mozilla::UniquePtr<Message> message) {
+ return channel_impl_->Send(std::move(message));
+}
+
+int32_t Channel::OtherPid() const { return channel_impl_->OtherPid(); }
+
+bool Channel::IsClosed() const { return channel_impl_->IsClosed(); }
+
+namespace {
+
+// Global atomic used to guarantee channel IDs are unique.
+mozilla::Atomic<int> g_last_id;
+
+} // namespace
+
+// static
+Channel::ChannelId Channel::GenerateVerifiedChannelID() {
+ // 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
+ // prevent hijacking the pipe name to spoof the client.
+ int secret;
+ do { // Guarantee we get a non-zero value.
+ secret = base::RandInt(0, std::numeric_limits<int>::max());
+ } while (secret == 0);
+ return StringPrintf(L"%d.%u.%d\\%d", base::GetCurrentProcId(), g_last_id++,
+ base::RandInt(0, std::numeric_limits<int32_t>::max()),
+ secret);
+}
+
+// static
+Channel::ChannelId Channel::ChannelIDForCurrentProcess() {
+ return CommandLine::ForCurrentProcess()->GetSwitchValue(
+ switches::kProcessChannelID);
+}
+
+// static
+bool Channel::CreateRawPipe(ChannelHandle* server, ChannelHandle* client) {
+ std::wstring pipe_name =
+ StringPrintf(L"\\\\.\\pipe\\gecko.%lu.%lu.%I64u", ::GetCurrentProcessId(),
+ ::GetCurrentThreadId(), mozilla::RandomUint64OrDie());
+ const DWORD kOpenMode =
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE;
+ const DWORD kPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE;
+ *server = mozilla::UniqueFileHandle(
+ ::CreateNamedPipeW(pipe_name.c_str(), kOpenMode, kPipeMode,
+ 1, // Max instances.
+ Channel::kReadBufferSize, // Output buffer size.
+ Channel::kReadBufferSize, // Input buffer size.
+ 5000, // Timeout in ms.
+ nullptr)); // Default security descriptor.
+ if (!server) {
+ NS_WARNING(
+ nsPrintfCString("CreateNamedPipeW Failed %lu", ::GetLastError()).get());
+ return false;
+ }
+
+ const DWORD kDesiredAccess = GENERIC_READ | GENERIC_WRITE;
+ // The SECURITY_ANONYMOUS flag means that the server side cannot impersonate
+ // the client, which is useful as both server & client may be unprivileged.
+ const DWORD kFlags =
+ SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS | FILE_FLAG_OVERLAPPED;
+ *client = mozilla::UniqueFileHandle(
+ ::CreateFileW(pipe_name.c_str(), kDesiredAccess, 0, nullptr,
+ OPEN_EXISTING, kFlags, nullptr));
+ if (!client) {
+ NS_WARNING(
+ nsPrintfCString("CreateFileW Failed %lu", ::GetLastError()).get());
+ return false;
+ }
+
+ // Since a client has connected, ConnectNamedPipe() should return zero and
+ // GetLastError() should return ERROR_PIPE_CONNECTED.
+ if (::ConnectNamedPipe(server->get(), nullptr) ||
+ ::GetLastError() != ERROR_PIPE_CONNECTED) {
+ NS_WARNING(
+ nsPrintfCString("ConnectNamedPipe Failed %lu", ::GetLastError()).get());
+ return false;
+ }
+ return true;
+}
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_channel_win.h b/ipc/chromium/src/chrome/common/ipc_channel_win.h
new file mode 100644
index 0000000000..5c141ebe34
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h
@@ -0,0 +1,202 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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.
+
+#ifndef CHROME_COMMON_IPC_CHANNEL_WIN_H_
+#define CHROME_COMMON_IPC_CHANNEL_WIN_H_
+
+#include "chrome/common/ipc_channel.h"
+#include "chrome/common/ipc_channel_capability.h"
+#include "chrome/common/ipc_message.h"
+
+#include <atomic>
+#include <string>
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/EventTargetCapability.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Queue.h"
+#include "mozilla/UniquePtr.h"
+
+namespace IPC {
+
+class Channel::ChannelImpl : public MessageLoopForIO::IOHandler {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_EVENT_TARGET(
+ ChannelImpl, IOThread().GetEventTarget());
+
+ using ChannelId = Channel::ChannelId;
+ using ChannelHandle = Channel::ChannelHandle;
+
+ // Mirror methods of Channel, see ipc_channel.h for description.
+ ChannelImpl(const ChannelId& channel_id, Mode mode, Listener* listener);
+ ChannelImpl(ChannelHandle pipe, Mode mode, Listener* listener);
+ bool Connect() MOZ_EXCLUDES(SendMutex());
+ void Close() MOZ_EXCLUDES(SendMutex());
+ void StartAcceptingHandles(Mode mode) MOZ_EXCLUDES(SendMutex());
+ Listener* set_listener(Listener* listener) {
+ IOThread().AssertOnCurrentThread();
+ chan_cap_.NoteOnIOThread();
+ Listener* old = listener_;
+ listener_ = listener;
+ return old;
+ }
+ // NOTE: `Send` may be called on threads other than the I/O thread.
+ bool Send(mozilla::UniquePtr<Message> message) MOZ_EXCLUDES(SendMutex());
+
+ int32_t OtherPid() {
+ IOThread().AssertOnCurrentThread();
+ chan_cap_.NoteOnIOThread();
+ return other_pid_;
+ }
+
+ // See the comment in ipc_channel.h for info on IsClosed()
+ // NOTE: `IsClosed` may be called on threads other than the I/O thread.
+ bool IsClosed() MOZ_EXCLUDES(SendMutex()) {
+ mozilla::MutexAutoLock lock(SendMutex());
+ chan_cap_.NoteSendMutex();
+ return pipe_ == INVALID_HANDLE_VALUE;
+ }
+
+ private:
+ ~ChannelImpl() {
+ IOThread().AssertOnCurrentThread();
+ if (pipe_ != INVALID_HANDLE_VALUE ||
+ other_process_ != INVALID_HANDLE_VALUE) {
+ Close();
+ }
+ }
+
+ void Init(Mode mode, Listener* listener)
+ MOZ_REQUIRES(SendMutex(), IOThread());
+
+ void OutputQueuePush(mozilla::UniquePtr<Message> msg)
+ MOZ_REQUIRES(SendMutex());
+ void OutputQueuePop() MOZ_REQUIRES(SendMutex());
+
+ const ChannelId PipeName(const ChannelId& channel_id, int32_t* secret) const;
+ bool CreatePipe(const ChannelId& channel_id, Mode mode)
+ MOZ_REQUIRES(SendMutex(), IOThread());
+ void SetOtherPid(int other_pid) MOZ_REQUIRES(IOThread())
+ MOZ_EXCLUDES(SendMutex());
+ bool EnqueueHelloMessage() MOZ_REQUIRES(SendMutex(), IOThread());
+ void CloseLocked() MOZ_REQUIRES(SendMutex(), IOThread());
+
+ bool ProcessConnection() MOZ_REQUIRES(SendMutex(), IOThread());
+ bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
+ DWORD bytes_read, bool was_pending)
+ MOZ_REQUIRES(IOThread());
+ bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
+ DWORD bytes_written, bool was_pending)
+ MOZ_REQUIRES(SendMutex());
+
+ // Called on a Message immediately before it is sent/recieved to transfer
+ // handles to the remote process, or accept handles from the remote process.
+ bool AcceptHandles(Message& msg) MOZ_REQUIRES(IOThread());
+ bool TransferHandles(Message& msg) MOZ_REQUIRES(SendMutex());
+
+ // MessageLoop::IOHandler implementation.
+ virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
+ DWORD bytes_transfered, DWORD error);
+
+ const ChannelCapability::Thread& IOThread() const
+ MOZ_RETURN_CAPABILITY(chan_cap_.IOThread()) {
+ return chan_cap_.IOThread();
+ }
+
+ ChannelCapability::Mutex& SendMutex()
+ MOZ_RETURN_CAPABILITY(chan_cap_.SendMutex()) {
+ return chan_cap_.SendMutex();
+ }
+
+ private:
+ // Compound capability of a Mutex and the IO thread.
+ ChannelCapability chan_cap_;
+
+ Mode mode_ MOZ_GUARDED_BY(IOThread());
+
+ struct State {
+ explicit State(ChannelImpl* channel);
+ ~State();
+ MessageLoopForIO::IOContext context;
+ // When there is pending I/O, this holds a strong reference to the
+ // ChannelImpl to prevent it from going away.
+ RefPtr<ChannelImpl> is_pending;
+ };
+
+ State input_state_ MOZ_GUARDED_BY(IOThread());
+ State output_state_ MOZ_GUARDED_BY(SendMutex());
+
+ HANDLE pipe_ MOZ_GUARDED_BY(chan_cap_) = INVALID_HANDLE_VALUE;
+
+ Listener* listener_ MOZ_GUARDED_BY(IOThread()) = nullptr;
+
+ // Messages to be sent are queued here.
+ mozilla::Queue<mozilla::UniquePtr<Message>, 64> output_queue_
+ MOZ_GUARDED_BY(SendMutex());
+
+ // If sending a message blocks then we use this iterator to keep track of
+ // where in the message we are. It gets reset when the message is finished
+ // sending.
+ mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_
+ MOZ_GUARDED_BY(SendMutex());
+
+ // We read from the pipe into this buffer
+ mozilla::UniquePtr<char[]> input_buf_ MOZ_GUARDED_BY(IOThread());
+ size_t input_buf_offset_ MOZ_GUARDED_BY(IOThread()) = 0;
+
+ // Large incoming messages that span multiple pipe buffers get built-up in the
+ // buffers of this message.
+ mozilla::UniquePtr<Message> incoming_message_ MOZ_GUARDED_BY(IOThread());
+
+ // Timer started when a MODE_SERVER channel begins waiting for a connection,
+ // and cancelled when the connection completes. Will produce an error if no
+ // connection occurs before the timeout.
+ nsCOMPtr<nsITimer> connect_timeout_ MOZ_GUARDED_BY(IOThread());
+
+ // Will be set to `true` until `Connect()` has been called, and, if in
+ // server-mode, the client has connected. The `input_state_` is used to wait
+ // for the client to connect in overlapped mode.
+ bool waiting_connect_ MOZ_GUARDED_BY(chan_cap_) = true;
+
+ // This flag is set when processing incoming messages. It is used to
+ // avoid recursing through ProcessIncomingMessages, which could cause
+ // problems. TODO(darin): make this unnecessary
+ bool processing_incoming_ MOZ_GUARDED_BY(IOThread()) = false;
+
+ // We keep track of the PID of the other side of this channel so that we can
+ // record this when generating logs of IPC messages.
+ int32_t other_pid_ MOZ_GUARDED_BY(chan_cap_) = -1;
+
+ // This is a unique per-channel value used to authenticate the client end of
+ // a connection. If the value is non-zero, the client passes it in the hello
+ // and the host validates. (We don't send the zero value to preserve IPC
+ // compatibility with existing clients that don't validate the channel.)
+ int32_t shared_secret_ MOZ_GUARDED_BY(IOThread()) = 0;
+
+ // In server-mode, we wait for the channel at the other side of the pipe to
+ // send us back our shared secret, if we are using one.
+ bool waiting_for_shared_secret_ MOZ_GUARDED_BY(IOThread()) = false;
+
+ // Whether or not to accept handles from a remote process, and whether this
+ // process is the privileged side of a IPC::Channel which can transfer
+ // handles.
+ bool accept_handles_ MOZ_GUARDED_BY(chan_cap_) = false;
+ bool privileged_ MOZ_GUARDED_BY(chan_cap_) = false;
+
+ // A privileged process handle used to transfer HANDLEs to and from the remote
+ // process. This will only be used if `privileged_` is set.
+ HANDLE other_process_ MOZ_GUARDED_BY(chan_cap_) = INVALID_HANDLE_VALUE;
+
+ DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
+};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_CHANNEL_WIN_H_
diff --git a/ipc/chromium/src/chrome/common/ipc_message.cc b/ipc/chromium/src/chrome/common/ipc_message.cc
new file mode 100644
index 0000000000..5343c67fc0
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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 "chrome/common/ipc_message.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "mojo/core/ports/event.h"
+
+#include <utility>
+
+#include "nsISupportsImpl.h"
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+const mojo::core::ports::UserMessage::TypeInfo Message::kUserMessageTypeInfo{};
+
+Message::~Message() { MOZ_COUNT_DTOR(IPC::Message); }
+
+Message::Message(int32_t routing_id, msgid_t type, uint32_t segment_capacity,
+ HeaderFlags flags)
+ : UserMessage(&kUserMessageTypeInfo),
+ Pickle(sizeof(Header), segment_capacity) {
+ MOZ_COUNT_CTOR(IPC::Message);
+ header()->routing = routing_id;
+ header()->type = type;
+ header()->flags = flags;
+ header()->num_handles = 0;
+ header()->txid = -1;
+ header()->seqno = 0;
+#if defined(OS_MACOSX)
+ header()->cookie = 0;
+ header()->num_send_rights = 0;
+#endif
+ header()->event_footer_size = 0;
+}
+
+Message::Message(const char* data, int data_len)
+ : UserMessage(&kUserMessageTypeInfo),
+ Pickle(sizeof(Header), data, data_len) {
+ MOZ_COUNT_CTOR(IPC::Message);
+}
+
+/*static*/ mozilla::UniquePtr<Message> Message::IPDLMessage(
+ int32_t routing_id, msgid_t type, uint32_t segment_capacity,
+ HeaderFlags flags) {
+ return mozilla::MakeUnique<Message>(routing_id, type, segment_capacity,
+ flags);
+}
+
+/*static*/ mozilla::UniquePtr<Message> Message::ForSyncDispatchError(
+ NestedLevel level) {
+ auto m = mozilla::MakeUnique<Message>(0, 0, 0, HeaderFlags(level));
+ auto& flags = m->header()->flags;
+ flags.SetSync();
+ flags.SetReply();
+ flags.SetReplyError();
+ return m;
+}
+
+void Message::WriteFooter(const void* data, uint32_t data_len) {
+ if (data_len == 0) {
+ return;
+ }
+
+ WriteBytes(data, data_len);
+}
+
+bool Message::ReadFooter(void* buffer, uint32_t buffer_len, bool truncate) {
+ if (buffer_len == 0) {
+ return true;
+ }
+
+ if (NS_WARN_IF(AlignInt(header()->payload_size) != header()->payload_size) ||
+ NS_WARN_IF(AlignInt(buffer_len) > header()->payload_size)) {
+ return false;
+ }
+
+ // Seek to the start of the footer, and read it in. We read in with a
+ // duplicate of the iterator so we can use it to truncate later.
+ uint32_t offset = header()->payload_size - AlignInt(buffer_len);
+ PickleIterator footer_iter(*this);
+ if (NS_WARN_IF(!IgnoreBytes(&footer_iter, offset))) {
+ return false;
+ }
+
+ PickleIterator read_iter(footer_iter);
+ bool ok = ReadBytesInto(&read_iter, buffer, buffer_len);
+
+ // If requested, truncate the buffer to the start of the footer.
+ if (truncate) {
+ Truncate(&footer_iter);
+ }
+ return ok;
+}
+
+bool Message::WriteFileHandle(mozilla::UniqueFileHandle handle) {
+ uint32_t handle_index = attached_handles_.Length();
+ WriteUInt32(handle_index);
+ if (handle_index == MAX_DESCRIPTORS_PER_MESSAGE) {
+ return false;
+ }
+ attached_handles_.AppendElement(std::move(handle));
+ return true;
+}
+
+bool Message::ConsumeFileHandle(PickleIterator* iter,
+ mozilla::UniqueFileHandle* handle) const {
+ uint32_t handle_index;
+ if (!ReadUInt32(iter, &handle_index)) {
+ return false;
+ }
+ if (handle_index >= attached_handles_.Length()) {
+ return false;
+ }
+ // NOTE: This mutates the underlying array, replacing the handle with an
+ // invalid handle.
+ *handle = std::exchange(attached_handles_[handle_index], nullptr);
+ return true;
+}
+
+void Message::SetAttachedFileHandles(
+ nsTArray<mozilla::UniqueFileHandle> handles) {
+ MOZ_DIAGNOSTIC_ASSERT(attached_handles_.IsEmpty());
+ attached_handles_ = std::move(handles);
+}
+
+uint32_t Message::num_handles() const { return attached_handles_.Length(); }
+
+void Message::WritePort(mozilla::ipc::ScopedPort port) {
+ uint32_t port_index = attached_ports_.Length();
+ WriteUInt32(port_index);
+ attached_ports_.AppendElement(std::move(port));
+}
+
+bool Message::ConsumePort(PickleIterator* iter,
+ mozilla::ipc::ScopedPort* port) const {
+ uint32_t port_index;
+ if (!ReadUInt32(iter, &port_index)) {
+ return false;
+ }
+ if (port_index >= attached_ports_.Length()) {
+ return false;
+ }
+ // NOTE: This mutates the underlying array, replacing the port with a consumed
+ // port.
+ *port = std::exchange(attached_ports_[port_index], {});
+ return true;
+}
+
+void Message::SetAttachedPorts(nsTArray<mozilla::ipc::ScopedPort> ports) {
+ MOZ_DIAGNOSTIC_ASSERT(attached_ports_.IsEmpty());
+ attached_ports_ = std::move(ports);
+}
+
+#if defined(OS_MACOSX)
+bool Message::WriteMachSendRight(mozilla::UniqueMachSendRight port) {
+ uint32_t index = attached_send_rights_.Length();
+ WriteUInt32(index);
+ if (index == MAX_DESCRIPTORS_PER_MESSAGE) {
+ return false;
+ }
+ attached_send_rights_.AppendElement(std::move(port));
+ return true;
+}
+
+bool Message::ConsumeMachSendRight(PickleIterator* iter,
+ mozilla::UniqueMachSendRight* port) const {
+ uint32_t index;
+ if (!ReadUInt32(iter, &index)) {
+ return false;
+ }
+ if (index >= attached_send_rights_.Length()) {
+ return false;
+ }
+ // NOTE: This mutates the underlying array, replacing the send right with a
+ // null right.
+ *port = std::exchange(attached_send_rights_[index], nullptr);
+ return true;
+}
+
+uint32_t Message::num_send_rights() const {
+ return attached_send_rights_.Length();
+}
+#endif
+
+bool Message::WillBeRoutedExternally(
+ mojo::core::ports::UserMessageEvent& event) {
+ if (!attached_ports_.IsEmpty()) {
+ // Explicitly attach any ports which were attached to this Message to this
+ // UserMessageEvent before we route it externally so that they can be
+ // transferred correctly. These ports will be recovered if needed in
+ // `GetMessage`.
+ MOZ_DIAGNOSTIC_ASSERT(
+ event.num_ports() == 0,
+ "Must not have previously attached ports to the UserMessageEvent");
+ event.ReservePorts(attached_ports_.Length());
+ for (size_t i = 0; i < event.num_ports(); ++i) {
+ event.ports()[i] = attached_ports_[i].Release().name();
+ }
+ attached_ports_.Clear();
+ }
+ return true;
+}
+
+void Message::AssertAsLargeAsHeader() const {
+ MOZ_DIAGNOSTIC_ASSERT(size() >= sizeof(Header));
+ MOZ_DIAGNOSTIC_ASSERT(CurrentSize() >= sizeof(Header));
+ // Our buffers should agree with what our header specifies.
+ MOZ_DIAGNOSTIC_ASSERT(size() == CurrentSize());
+}
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_message.h b/ipc/chromium/src/chrome/common/ipc_message.h
new file mode 100644
index 0000000000..15188c8280
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_message.h
@@ -0,0 +1,424 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_IPC_MESSAGE_H__
+#define CHROME_COMMON_IPC_MESSAGE_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/pickle.h"
+#include "mojo/core/ports/user_message.h"
+#include "mojo/core/ports/port_ref.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/ipc/ScopedPort.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+#ifdef FUZZING_SNAPSHOT
+namespace fuzzing {
+class IPCFuzzController;
+}
+#endif
+
+namespace ipc {
+class MiniTransceiver;
+}
+} // namespace mozilla
+
+namespace IPC {
+
+//------------------------------------------------------------------------------
+
+// Generated by IPDL compiler
+const char* StringFromIPCMessageType(uint32_t aMessageType);
+
+class Channel;
+class Message;
+class MessageReader;
+class MessageWriter;
+struct LogData;
+
+class Message : public mojo::core::ports::UserMessage, public Pickle {
+ public:
+ static const TypeInfo kUserMessageTypeInfo;
+
+ typedef uint32_t msgid_t;
+
+ enum NestedLevel {
+ NOT_NESTED = 1,
+ NESTED_INSIDE_SYNC = 2,
+ NESTED_INSIDE_CPOW = 3
+ };
+
+ enum PriorityValue {
+ NORMAL_PRIORITY = 0,
+ INPUT_PRIORITY = 1,
+ VSYNC_PRIORITY = 2,
+ MEDIUMHIGH_PRIORITY = 3,
+ CONTROL_PRIORITY = 4,
+ };
+
+ enum MessageCompression {
+ COMPRESSION_NONE,
+ COMPRESSION_ENABLED,
+ COMPRESSION_ALL
+ };
+
+ enum Sync {
+ SYNC = 0,
+ ASYNC = 1,
+ };
+
+ enum Constructor {
+ NOT_CONSTRUCTOR = 0,
+ CONSTRUCTOR = 1,
+ };
+
+ enum Reply {
+ NOT_REPLY = 0,
+ REPLY = 1,
+ };
+
+ enum LazySend {
+ EAGER_SEND = 0,
+ LAZY_SEND = 1,
+ };
+
+ // The hard limit of handles or file descriptors allowed in a single message.
+ static constexpr size_t MAX_DESCRIPTORS_PER_MESSAGE = 32767;
+
+ class HeaderFlags {
+ friend class Message;
+#ifdef FUZZING_SNAPSHOT
+ // IPCFuzzController calls various private API functions on the header.
+ friend class mozilla::fuzzing::IPCFuzzController;
+#endif
+
+ enum {
+ NESTED_MASK = 0x0003,
+ PRIO_MASK = 0x001C,
+ SYNC_BIT = 0x0020,
+ REPLY_BIT = 0x0040,
+ REPLY_ERROR_BIT = 0x0080,
+ LAZY_SEND_BIT = 0x0100,
+ COMPRESS_BIT = 0x0200,
+ COMPRESSALL_BIT = 0x0400,
+ CONSTRUCTOR_BIT = 0x0800,
+ RELAY_BIT = 0x1000,
+ };
+
+ public:
+ constexpr HeaderFlags() : mFlags(NOT_NESTED) {}
+
+ explicit constexpr HeaderFlags(NestedLevel level) : mFlags(level) {}
+
+ constexpr HeaderFlags(NestedLevel level, PriorityValue priority,
+ MessageCompression compression, LazySend lazy_send,
+ Constructor constructor, Sync sync, Reply reply)
+ : mFlags(level | (priority << 2) |
+ (compression == COMPRESSION_ENABLED ? COMPRESS_BIT
+ : compression == COMPRESSION_ALL ? COMPRESSALL_BIT
+ : 0) |
+ (lazy_send == LAZY_SEND ? LAZY_SEND_BIT : 0) |
+ (constructor == CONSTRUCTOR ? CONSTRUCTOR_BIT : 0) |
+ (sync == SYNC ? SYNC_BIT : 0) |
+ (reply == REPLY ? REPLY_BIT : 0)) {}
+
+ NestedLevel Level() const {
+ return static_cast<NestedLevel>(mFlags & NESTED_MASK);
+ }
+
+ PriorityValue Priority() const {
+ return static_cast<PriorityValue>((mFlags & PRIO_MASK) >> 2);
+ }
+
+ MessageCompression Compression() const {
+ return ((mFlags & COMPRESS_BIT) ? COMPRESSION_ENABLED
+ : (mFlags & COMPRESSALL_BIT) ? COMPRESSION_ALL
+ : COMPRESSION_NONE);
+ }
+
+ bool IsLazySend() const { return (mFlags & LAZY_SEND_BIT) != 0; }
+
+ bool IsConstructor() const { return (mFlags & CONSTRUCTOR_BIT) != 0; }
+ bool IsSync() const { return (mFlags & SYNC_BIT) != 0; }
+ bool IsReply() const { return (mFlags & REPLY_BIT) != 0; }
+
+ bool IsReplyError() const { return (mFlags & REPLY_ERROR_BIT) != 0; }
+ bool IsRelay() const { return (mFlags & RELAY_BIT) != 0; }
+
+ private:
+ void SetConstructor() { mFlags |= CONSTRUCTOR_BIT; }
+ void SetSync() { mFlags |= SYNC_BIT; }
+ void SetReply() { mFlags |= REPLY_BIT; }
+ void SetReplyError() { mFlags |= REPLY_ERROR_BIT; }
+ void SetRelay(bool relay) {
+ if (relay) {
+ mFlags |= RELAY_BIT;
+ } else {
+ mFlags &= ~RELAY_BIT;
+ }
+ }
+
+ uint32_t mFlags;
+ };
+
+ virtual ~Message();
+
+ // Initialize a message with a user-defined type, priority value, and
+ // destination WebView ID.
+ Message(int32_t routing_id, msgid_t type,
+ uint32_t segmentCapacity = 0, // 0 for the default capacity.
+ HeaderFlags flags = HeaderFlags());
+
+ Message(const char* data, int data_len);
+
+ Message(const Message&) = delete;
+ Message(Message&&) = delete;
+ Message& operator=(const Message&) = delete;
+ Message& operator=(Message&&) = delete;
+
+ // Helper method for the common case (default segmentCapacity, recording
+ // the write latency of messages) of IPDL message creation. This helps
+ // move the malloc and some of the parameter setting out of autogenerated
+ // code.
+ static mozilla::UniquePtr<Message> IPDLMessage(int32_t routing_id,
+ msgid_t type,
+ uint32_t segmentCapacity,
+ HeaderFlags flags);
+
+ // One-off constructors for special error-handling messages.
+ static mozilla::UniquePtr<Message> ForSyncDispatchError(NestedLevel level);
+
+ NestedLevel nested_level() const { return header()->flags.Level(); }
+
+ PriorityValue priority() const { return header()->flags.Priority(); }
+
+ bool is_constructor() const { return header()->flags.IsConstructor(); }
+
+ // True if this is a synchronous message.
+ bool is_sync() const { return header()->flags.IsSync(); }
+
+ MessageCompression compress_type() const {
+ return header()->flags.Compression();
+ }
+
+ bool is_lazy_send() const { return header()->flags.IsLazySend(); }
+
+ bool is_reply() const { return header()->flags.IsReply(); }
+
+ bool is_reply_error() const { return header()->flags.IsReplyError(); }
+
+ msgid_t type() const { return header()->type; }
+
+ int32_t routing_id() const { return header()->routing; }
+
+ void set_routing_id(int32_t new_id) { header()->routing = new_id; }
+
+ int32_t transaction_id() const { return header()->txid; }
+
+ void set_transaction_id(int32_t txid) { header()->txid = txid; }
+
+ int32_t seqno() const { return header()->seqno; }
+
+ void set_seqno(int32_t aSeqno) { header()->seqno = aSeqno; }
+
+ const char* name() const { return StringFromIPCMessageType(type()); }
+
+ uint32_t num_handles() const;
+
+ bool is_relay() const { return header()->flags.IsRelay(); }
+ void set_relay(bool new_relay) { header()->flags.SetRelay(new_relay); }
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj, void (T::*func)()) {
+ (obj->*func)();
+ return true;
+ }
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj, void (T::*func)() const) {
+ (obj->*func)();
+ return true;
+ }
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&)) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ template <class T>
+ static bool Dispatch(const Message* msg, T* obj,
+ void (T::*func)(const Message&) const) {
+ (obj->*func)(*msg);
+ return true;
+ }
+
+ // We should not be sending messages that are smaller than our header size.
+ void AssertAsLargeAsHeader() const;
+
+ // UserMessage implementation
+ size_t GetSizeIfSerialized() const override { return size(); }
+ bool WillBeRoutedExternally(mojo::core::ports::UserMessageEvent&) override;
+
+ // Write the given footer bytes to the end of the current message. The
+ // footer's `data_len` will be padded to a multiple of 4 bytes.
+ void WriteFooter(const void* data, uint32_t data_len);
+ // Read a footer written with `WriteFooter` from the end of the message, given
+ // a buffer and the length of the footer. If `truncate` is true, the message
+ // will be truncated, removing the footer.
+ [[nodiscard]] bool ReadFooter(void* buffer, uint32_t buffer_len,
+ bool truncate);
+
+ uint32_t event_footer_size() const { return header()->event_footer_size; }
+
+ void set_event_footer_size(uint32_t size) {
+ header()->event_footer_size = size;
+ }
+
+ static int HeaderSize() { return sizeof(Header); }
+
+ // Figure out how big the message starting at range_start is. Returns 0 if
+ // there's no enough data to determine (i.e., if [range_start, range_end) does
+ // not contain enough of the message header to know the size).
+ static uint32_t MessageSize(const char* range_start, const char* range_end) {
+ return Pickle::MessageSize(HeaderSize(), range_start, range_end);
+ }
+
+ bool WriteFileHandle(mozilla::UniqueFileHandle handle);
+
+ // WARNING: This method is marked as `const` so it can be called when
+ // deserializing the message, but will mutate it, consuming the handle.
+ bool ConsumeFileHandle(PickleIterator* iter,
+ mozilla::UniqueFileHandle* handle) const;
+
+ // Called when receiving an IPC message to attach file handles which were
+ // received from IPC. Must only be called when there are no handles on this
+ // IPC::Message.
+ void SetAttachedFileHandles(nsTArray<mozilla::UniqueFileHandle> handles);
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+ void set_fd_cookie(uint32_t cookie) { header()->cookie = cookie; }
+ uint32_t fd_cookie() const { return header()->cookie; }
+#endif
+
+ void WritePort(mozilla::ipc::ScopedPort port);
+
+ // This method consumes the port from the message, preventing the message's
+ // destructor from destroying the port and meaning that future attempts to
+ // read this port will instead produce an invalid port.
+ //
+ // WARNING: This method is marked as `const` so it can be called when
+ // deserializing the message, but will mutate the message.
+ bool ConsumePort(PickleIterator* iter, mozilla::ipc::ScopedPort* port) const;
+
+ // Called when loading an IPC message to attach ports which were recieved form
+ // IPC. Must only be called when there are no ports on this IPC::Message.
+ void SetAttachedPorts(nsTArray<mozilla::ipc::ScopedPort> ports);
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+ bool WriteMachSendRight(mozilla::UniqueMachSendRight port);
+
+ // WARNING: This method is marked as `const` so it can be called when
+ // deserializing the message, but will mutate it, consuming the send rights.
+ bool ConsumeMachSendRight(PickleIterator* iter,
+ mozilla::UniqueMachSendRight* port) const;
+
+ uint32_t num_send_rights() const;
+#endif
+
+ uint32_t num_relayed_attachments() const {
+#if defined(OS_WIN)
+ return num_handles();
+#elif defined(OS_MACOSX) || defined(OS_IOS)
+ return num_send_rights();
+#else
+ return 0;
+#endif
+ }
+
+#ifdef FUZZING_SNAPSHOT
+ bool IsFuzzMsg() { return isFuzzMsg; }
+ void SetFuzzMsg() { isFuzzMsg = true; }
+#endif
+
+ friend class Channel;
+ friend class MessageReplyDeserializer;
+ friend class SyncMessage;
+ friend class mozilla::ipc::MiniTransceiver;
+
+#if !defined(OS_MACOSX) && !defined(OS_IOS) && !defined(FUZZING_SNAPSHOT)
+ protected:
+#endif
+
+ struct Header : Pickle::Header {
+ int32_t routing; // ID of the view that this message is destined for
+ msgid_t type; // specifies the user-defined message type
+ HeaderFlags flags; // specifies control flags for the message
+ uint32_t num_handles; // the number of handles included with this message
+#if defined(OS_MACOSX) || defined(OS_IOS)
+ uint32_t cookie; // cookie to ACK that the descriptors have been read.
+ uint32_t num_send_rights; // the number of mach send rights included with
+ // this message
+#endif
+ // For sync messages, a transaction ID for message ordering.
+ int32_t txid;
+ // Sequence number
+ int32_t seqno;
+ // Size of the message's event footer
+ uint32_t event_footer_size;
+ };
+
+ Header* header() { return headerT<Header>(); }
+ const Header* header() const { return headerT<Header>(); }
+
+ // The set of file handles which are attached to this message.
+ //
+ // Mutable, as this array can be mutated during `ReadHandle` when
+ // deserializing a message.
+ mutable nsTArray<mozilla::UniqueFileHandle> attached_handles_;
+
+ // The set of mojo ports which are attached to this message.
+ //
+ // Mutable, as this array can be mutated during `ConsumePort` when
+ // deserializing a message.
+ mutable nsTArray<mozilla::ipc::ScopedPort> attached_ports_;
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+ // The set of mach send rights which are attached to this message.
+ //
+ // Mutable, as this array can be mutated during `ConsumeMachSendRight` when
+ // deserializing a message.
+ mutable nsTArray<mozilla::UniqueMachSendRight> attached_send_rights_;
+#endif
+
+#ifdef FUZZING_SNAPSHOT
+ bool isFuzzMsg = false;
+#endif
+};
+
+//------------------------------------------------------------------------------
+
+} // namespace IPC
+
+enum SpecialRoutingIDs {
+ // indicates that we don't have a routing ID yet.
+ MSG_ROUTING_NONE = kint32min,
+
+ // indicates a general message not sent to a particular tab.
+ MSG_ROUTING_CONTROL = kint32max
+};
+
+#define IPC_REPLY_ID 0xFFF0 // Special message id for replies
+#define IPC_LOGGING_ID 0xFFF1 // Special message id for logging
+
+#endif // CHROME_COMMON_IPC_MESSAGE_H__
diff --git a/ipc/chromium/src/chrome/common/ipc_message_utils.cc b/ipc/chromium/src/chrome/common/ipc_message_utils.cc
new file mode 100644
index 0000000000..49cb277e5f
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_message_utils.cc
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "chrome/common/ipc_message_utils.h"
+#include "mozilla/ipc/SharedMemory.h"
+#include "mozilla/ipc/SharedMemoryBasic.h"
+
+namespace IPC {
+
+static uint32_t kShmemThreshold = 64 * 1024;
+
+MessageBufferWriter::MessageBufferWriter(MessageWriter* writer,
+ uint32_t full_len)
+ : writer_(writer) {
+ if (full_len > kShmemThreshold) {
+ shmem_ = new mozilla::ipc::SharedMemoryBasic();
+ if (!shmem_->Create(full_len)) {
+ writer->FatalError("SharedMemory::Create failed!");
+ return;
+ }
+ if (!shmem_->Map(full_len)) {
+ writer->FatalError("SharedMemory::Map failed");
+ return;
+ }
+ if (!shmem_->WriteHandle(writer)) {
+ writer->FatalError("SharedMemory::WriterHandle failed");
+ return;
+ }
+ buffer_ = reinterpret_cast<char*>(shmem_->memory());
+ }
+ remaining_ = full_len;
+}
+
+MessageBufferWriter::~MessageBufferWriter() {
+ if (remaining_ != 0) {
+ writer_->FatalError("didn't fully write message buffer");
+ }
+}
+
+bool MessageBufferWriter::WriteBytes(const void* data, uint32_t len) {
+ MOZ_RELEASE_ASSERT(len == remaining_ || (len % 4) == 0,
+ "all writes except for the final write must be a multiple "
+ "of 4 bytes in length due to padding");
+ if (len > remaining_) {
+ writer_->FatalError("MessageBufferWriter overrun");
+ return false;
+ }
+ remaining_ -= len;
+ // If we're serializing using a shared memory region, `buffer_` will be
+ // initialized to point into that region.
+ if (buffer_) {
+ memcpy(buffer_, data, len);
+ buffer_ += len;
+ return true;
+ }
+ return writer_->WriteBytes(data, len);
+}
+
+MessageBufferReader::MessageBufferReader(MessageReader* reader,
+ uint32_t full_len)
+ : reader_(reader) {
+ if (full_len > kShmemThreshold) {
+ shmem_ = new mozilla::ipc::SharedMemoryBasic();
+ if (!shmem_->ReadHandle(reader)) {
+ reader->FatalError("SharedMemory::ReadHandle failed!");
+ return;
+ }
+ if (!shmem_->Map(full_len)) {
+ reader->FatalError("SharedMemory::Map failed");
+ return;
+ }
+ buffer_ = reinterpret_cast<const char*>(shmem_->memory());
+ }
+ remaining_ = full_len;
+}
+
+MessageBufferReader::~MessageBufferReader() {
+ if (remaining_ != 0) {
+ reader_->FatalError("didn't fully write message buffer");
+ }
+}
+
+bool MessageBufferReader::ReadBytesInto(void* data, uint32_t len) {
+ MOZ_RELEASE_ASSERT(len == remaining_ || (len % 4) == 0,
+ "all reads except for the final read must be a multiple "
+ "of 4 bytes in length due to padding");
+ if (len > remaining_) {
+ reader_->FatalError("MessageBufferReader overrun");
+ return false;
+ }
+ remaining_ -= len;
+ // If we're serializing using a shared memory region, `buffer_` will be
+ // initialized to point into that region.
+ if (buffer_) {
+ memcpy(data, buffer_, len);
+ buffer_ += len;
+ return true;
+ }
+ return reader_->ReadBytesInto(data, len);
+}
+
+} // namespace IPC
diff --git a/ipc/chromium/src/chrome/common/ipc_message_utils.h b/ipc/chromium/src/chrome/common/ipc_message_utils.h
new file mode 100644
index 0000000000..90b94feaed
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h
@@ -0,0 +1,1093 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_IPC_MESSAGE_UTILS_H_
+#define CHROME_COMMON_IPC_MESSAGE_UTILS_H_
+
+#include <cstdint>
+#include <iterator>
+#include <map>
+#include <unordered_map>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "ErrorList.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/pickle.h"
+#include "base/string_util.h"
+#include "build/build_config.h"
+#include "chrome/common/ipc_message.h"
+#include "mozilla/CheckedInt.h"
+#include "mozilla/IntegerRange.h"
+
+#if defined(OS_WIN)
+# include <windows.h>
+#endif
+
+template <typename T>
+class RefPtr;
+template <typename T>
+class nsCOMPtr;
+
+namespace mozilla::ipc {
+class IProtocol;
+template <typename P>
+struct IPDLParamTraits;
+class SharedMemory;
+
+// Implemented in ProtocolUtils.cpp
+MOZ_NEVER_INLINE void PickleFatalError(const char* aMsg, IProtocol* aActor);
+} // namespace mozilla::ipc
+
+namespace IPC {
+
+/**
+ * Context used to serialize into an IPC::Message. Provides relevant context
+ * used when serializing.
+ */
+class MOZ_STACK_CLASS MessageWriter final {
+ public:
+ explicit MessageWriter(Message& message,
+ mozilla::ipc::IProtocol* actor = nullptr)
+ : message_(message), actor_(actor) {}
+
+ MessageWriter(const MessageWriter&) = delete;
+ MessageWriter& operator=(const MessageWriter&) = delete;
+
+ mozilla::ipc::IProtocol* GetActor() const { return actor_; }
+
+#define FORWARD_WRITE(name, type) \
+ bool Write##name(const type& result) { return message_.Write##name(result); }
+
+ FORWARD_WRITE(Bool, bool)
+ FORWARD_WRITE(Int16, int16_t)
+ FORWARD_WRITE(UInt16, uint16_t)
+ FORWARD_WRITE(Int, int)
+ FORWARD_WRITE(Long, long)
+ FORWARD_WRITE(ULong, unsigned long)
+ FORWARD_WRITE(Int32, int32_t)
+ FORWARD_WRITE(UInt32, uint32_t)
+ FORWARD_WRITE(Int64, int64_t)
+ FORWARD_WRITE(UInt64, uint64_t)
+ FORWARD_WRITE(Double, double)
+ FORWARD_WRITE(IntPtr, intptr_t)
+ FORWARD_WRITE(UnsignedChar, unsigned char)
+ FORWARD_WRITE(String, std::string)
+ FORWARD_WRITE(WString, std::wstring)
+
+#undef FORWARD_WRITE
+
+ bool WriteData(const char* data, uint32_t length) {
+ return message_.WriteData(data, length);
+ }
+
+ bool WriteBytes(const void* data, uint32_t data_len) {
+ return message_.WriteBytes(data, data_len);
+ }
+
+ bool WriteBytesZeroCopy(void* data, uint32_t data_len, uint32_t capacity) {
+ return message_.WriteBytesZeroCopy(data, data_len, capacity);
+ }
+
+ bool WriteSentinel(uint32_t sentinel) {
+ return message_.WriteSentinel(sentinel);
+ }
+
+ bool WriteFileHandle(mozilla::UniqueFileHandle handle) {
+ return message_.WriteFileHandle(std::move(handle));
+ }
+
+ void WritePort(mozilla::ipc::ScopedPort port) {
+ message_.WritePort(std::move(port));
+ }
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+ bool WriteMachSendRight(mozilla::UniqueMachSendRight port) {
+ return message_.WriteMachSendRight(std::move(port));
+ }
+#endif
+
+ void FatalError(const char* aErrorMsg) const {
+ mozilla::ipc::PickleFatalError(aErrorMsg, actor_);
+ }
+
+ private:
+ Message& message_;
+ mozilla::ipc::IProtocol* actor_;
+};
+
+/**
+ * Context used to read data from an IPC::Message. Provides relevant context
+ * used when deserializing and tracks iteration.
+ */
+class MOZ_STACK_CLASS MessageReader final {
+ public:
+ explicit MessageReader(const Message& message,
+ mozilla::ipc::IProtocol* actor = nullptr)
+ : message_(message), iter_(message), actor_(actor) {}
+
+ MessageReader(const MessageReader&) = delete;
+ MessageReader& operator=(const MessageReader&) = delete;
+
+ mozilla::ipc::IProtocol* GetActor() const { return actor_; }
+
+#define FORWARD_READ(name, type) \
+ [[nodiscard]] bool Read##name(type* result) { \
+ return message_.Read##name(&iter_, result); \
+ }
+
+ FORWARD_READ(Bool, bool)
+ FORWARD_READ(Int16, int16_t)
+ FORWARD_READ(UInt16, uint16_t)
+ FORWARD_READ(Short, short)
+ FORWARD_READ(Int, int)
+ FORWARD_READ(Long, long)
+ FORWARD_READ(ULong, unsigned long)
+ FORWARD_READ(Int32, int32_t)
+ FORWARD_READ(UInt32, uint32_t)
+ FORWARD_READ(Int64, int64_t)
+ FORWARD_READ(UInt64, uint64_t)
+ FORWARD_READ(Double, double)
+ FORWARD_READ(IntPtr, intptr_t)
+ FORWARD_READ(UnsignedChar, unsigned char)
+ FORWARD_READ(String, std::string)
+ FORWARD_READ(WString, std::wstring)
+
+ // Special version of ReadInt() which rejects negative values
+ FORWARD_READ(Length, int);
+
+#undef FORWARD_READ
+
+ [[nodiscard]] bool ReadBytesInto(void* data, uint32_t length) {
+ return message_.ReadBytesInto(&iter_, data, length);
+ }
+
+ [[nodiscard]] bool IgnoreBytes(uint32_t length) {
+ return message_.IgnoreBytes(&iter_, length);
+ }
+
+ [[nodiscard]] bool ReadSentinel(uint32_t sentinel) {
+ return message_.ReadSentinel(&iter_, sentinel);
+ }
+
+ bool IgnoreSentinel() { return message_.IgnoreSentinel(&iter_); }
+
+ bool HasBytesAvailable(uint32_t len) {
+ return message_.HasBytesAvailable(&iter_, len);
+ }
+
+ void EndRead() { message_.EndRead(iter_, message_.type()); }
+
+ [[nodiscard]] bool ConsumeFileHandle(mozilla::UniqueFileHandle* handle) {
+ return message_.ConsumeFileHandle(&iter_, handle);
+ }
+
+ [[nodiscard]] bool ConsumePort(mozilla::ipc::ScopedPort* port) {
+ return message_.ConsumePort(&iter_, port);
+ }
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+ [[nodiscard]] bool ConsumeMachSendRight(mozilla::UniqueMachSendRight* port) {
+ return message_.ConsumeMachSendRight(&iter_, port);
+ }
+#endif
+
+ void FatalError(const char* aErrorMsg) const {
+ mozilla::ipc::PickleFatalError(aErrorMsg, actor_);
+ }
+
+ private:
+ const Message& message_;
+ PickleIterator iter_;
+ mozilla::ipc::IProtocol* actor_;
+};
+
+namespace detail {
+
+// Helper for checking `T::kHasDeprecatedReadParamPrivateConstructor` using a
+// fallback when the member isn't defined.
+template <typename T>
+inline constexpr auto HasDeprecatedReadParamPrivateConstructor(int)
+ -> decltype(T::kHasDeprecatedReadParamPrivateConstructor) {
+ return T::kHasDeprecatedReadParamPrivateConstructor;
+}
+
+template <typename T>
+inline constexpr bool HasDeprecatedReadParamPrivateConstructor(...) {
+ return false;
+}
+
+} // namespace detail
+
+/**
+ * Result type returned from some `ParamTraits<T>::Read` implementations, and
+ * from `IPC::ReadParam<T>(MessageReader*)`. Either contains the value or
+ * indicates a failure to deserialize.
+ *
+ * This type can be thought of as a variant on `Maybe<T>`, except that it
+ * unconditionally constructs the underlying value if it is default
+ * constructible. This helps keep code size down, especially when calling
+ * outparameter-based ReadParam implementations (bug 1815177).
+ */
+template <typename T,
+ bool = std::is_default_constructible_v<T> ||
+ detail::HasDeprecatedReadParamPrivateConstructor<T>(0)>
+class ReadResult {
+ public:
+ ReadResult() = default;
+
+ template <typename U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
+ MOZ_IMPLICIT ReadResult(U&& aData)
+ : mIsOk(true), mData(std::forward<U>(aData)) {}
+
+ template <typename... Args>
+ explicit ReadResult(std::in_place_t, Args&&... aArgs)
+ : mIsOk(true), mData(std::forward<Args>(aArgs)...) {}
+
+ ReadResult(const ReadResult&) = default;
+ ReadResult(ReadResult&&) = default;
+
+ template <typename U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
+ MOZ_IMPLICIT ReadResult& operator=(U&& aData) {
+ mIsOk = true;
+ mData = std::forward<U>(aData);
+ return *this;
+ }
+
+ ReadResult& operator=(const ReadResult&) = default;
+ ReadResult& operator=(ReadResult&&) noexcept = default;
+
+ // Check if the ReadResult contains a valid value.
+ explicit operator bool() const { return isOk(); }
+ bool isOk() const { return mIsOk; }
+
+ // Get the data from this ReadResult.
+ T& get() {
+ MOZ_ASSERT(mIsOk);
+ return mData;
+ }
+ const T& get() const {
+ MOZ_ASSERT(mIsOk);
+ return mData;
+ }
+
+ T& operator*() { return get(); }
+ const T& operator*() const { return get(); }
+
+ T* operator->() { return &get(); }
+ const T* operator->() const { return &get(); }
+
+ // Try to extract a `Maybe<T>` from this ReadResult.
+ mozilla::Maybe<T> TakeMaybe() {
+ if (mIsOk) {
+ mIsOk = false;
+ return mozilla::Some(std::move(mData));
+ }
+ return mozilla::Nothing();
+ }
+
+ // Get the underlying data from this ReadResult, even if not OK.
+ //
+ // This is only available for types which are default constructible, and is
+ // used to optimize old-style `ReadParam` calls.
+ T& GetStorage() { return mData; }
+
+ // Compliment to `GetStorage` used to set the ReadResult into an OK state
+ // without constructing the underlying value.
+ void SetOk(bool aIsOk) { mIsOk = aIsOk; }
+
+ private:
+ bool mIsOk = false;
+ T mData{};
+};
+
+template <typename T>
+class ReadResult<T, false> {
+ public:
+ ReadResult() = default;
+
+ template <typename U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
+ MOZ_IMPLICIT ReadResult(U&& aData)
+ : mData(std::in_place, std::forward<U>(aData)) {}
+
+ template <typename... Args>
+ explicit ReadResult(std::in_place_t, Args&&... aArgs)
+ : mData(std::in_place, std::forward<Args>(aArgs)...) {}
+
+ ReadResult(const ReadResult&) = default;
+ ReadResult(ReadResult&&) = default;
+
+ template <typename U, std::enable_if_t<std::is_convertible_v<U, T>, int> = 0>
+ MOZ_IMPLICIT ReadResult& operator=(U&& aData) {
+ mData.reset();
+ mData.emplace(std::forward<U>(aData));
+ return *this;
+ }
+
+ ReadResult& operator=(const ReadResult&) = default;
+ ReadResult& operator=(ReadResult&&) noexcept = default;
+
+ // Check if the ReadResult contains a valid value.
+ explicit operator bool() const { return isOk(); }
+ bool isOk() const { return mData.isSome(); }
+
+ // Get the data from this ReadResult.
+ T& get() { return mData.ref(); }
+ const T& get() const { return mData.ref(); }
+
+ T& operator*() { return get(); }
+ const T& operator*() const { return get(); }
+
+ T* operator->() { return &get(); }
+ const T* operator->() const { return &get(); }
+
+ // Try to extract a `Maybe<T>` from this ReadResult.
+ mozilla::Maybe<T> TakeMaybe() { return std::move(mData); }
+
+ // These methods are only available if the type is default constructible.
+ T& GetStorage() = delete;
+ void SetOk(bool aIsOk) = delete;
+
+ private:
+ mozilla::Maybe<T> mData;
+};
+
+//-----------------------------------------------------------------------------
+// An iterator class for reading the fields contained within a Message.
+
+class MessageIterator {
+ public:
+ explicit MessageIterator(const Message& m) : msg_(m), iter_(m) {}
+ int NextInt() const {
+ int val;
+ if (!msg_.ReadInt(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+ intptr_t NextIntPtr() const {
+ intptr_t val;
+ if (!msg_.ReadIntPtr(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+ const std::string NextString() const {
+ std::string val;
+ if (!msg_.ReadString(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+ const std::wstring NextWString() const {
+ std::wstring val;
+ if (!msg_.ReadWString(&iter_, &val)) NOTREACHED();
+ return val;
+ }
+
+ private:
+ const Message& msg_;
+ mutable PickleIterator iter_;
+};
+
+//-----------------------------------------------------------------------------
+// ParamTraits specializations, etc.
+//
+// The full set of types ParamTraits is specialized upon contains *possibly*
+// repeated types: unsigned long may be uint32_t or size_t, unsigned long long
+// may be uint64_t or size_t, nsresult may be uint32_t, and so on. You can't
+// have ParamTraits<unsigned int> *and* ParamTraits<uint32_t> if unsigned int
+// is uint32_t -- that's multiple definitions, and you can only have one.
+//
+// You could use #ifs and macro conditions to avoid duplicates, but they'd be
+// hairy: heavily dependent upon OS and compiler author choices, forced to
+// address all conflicts by hand. Happily there's a better way. The basic
+// idea looks like this, where T -> U represents T inheriting from U:
+//
+// class ParamTraits<P>
+// |
+// --> class ParamTraits1<P>
+// |
+// --> class ParamTraits2<P>
+// |
+// --> class ParamTraitsN<P> // or however many levels
+//
+// The default specialization of ParamTraits{M}<P> is an empty class that
+// inherits from ParamTraits{M + 1}<P> (or nothing in the base case).
+//
+// Now partition the set of parameter types into sets without duplicates.
+// Assign each set of types to a level M. Then specialize ParamTraitsM for
+// each of those types. A reference to ParamTraits<P> will consist of some
+// number of empty classes inheriting in sequence, ending in a non-empty
+// ParamTraits{N}<P>. It's okay for the parameter types to be duplicative:
+// either name of a type will resolve to the same ParamTraits{N}<P>.
+//
+// The nice thing is that because templates are instantiated lazily, if we
+// indeed have uint32_t == unsigned int, say, with the former in level N and
+// the latter in M > N, ParamTraitsM<unsigned int> won't be created (as long as
+// nobody uses ParamTraitsM<unsigned int>, but why would you), and no duplicate
+// code will be compiled or extra symbols generated. It's as efficient at
+// runtime as manually figuring out and avoiding conflicts by #ifs.
+//
+// The scheme we follow below names the various classes according to the types
+// in them, and the number of ParamTraits levels is larger, but otherwise it's
+// exactly the above idea.
+//
+
+template <class P>
+struct ParamTraits;
+
+template <typename P>
+inline void WriteParam(MessageWriter* writer, P&& p) {
+ ParamTraits<std::decay_t<P>>::Write(writer, std::forward<P>(p));
+}
+
+namespace detail {
+
+template <typename P>
+inline constexpr auto ParamTraitsReadUsesOutParam()
+ -> decltype(ParamTraits<P>::Read(std::declval<MessageReader*>(),
+ std::declval<P*>())) {
+ return true;
+}
+
+template <typename P>
+inline constexpr auto ParamTraitsReadUsesOutParam()
+ -> decltype(ParamTraits<P>::Read(std::declval<MessageReader*>()), bool{}) {
+ return false;
+}
+
+} // namespace detail
+
+template <typename P>
+inline bool WARN_UNUSED_RESULT ReadParam(MessageReader* reader, P* p) {
+ if constexpr (!detail::ParamTraitsReadUsesOutParam<P>()) {
+ auto maybe = ParamTraits<P>::Read(reader);
+ if (maybe) {
+ *p = std::move(*maybe);
+ return true;
+ }
+ return false;
+ } else {
+ return ParamTraits<P>::Read(reader, p);
+ }
+}
+
+template <typename P>
+inline ReadResult<P> WARN_UNUSED_RESULT ReadParam(MessageReader* reader) {
+ if constexpr (!detail::ParamTraitsReadUsesOutParam<P>()) {
+ return ParamTraits<P>::Read(reader);
+ } else {
+ ReadResult<P> p;
+ p.SetOk(ParamTraits<P>::Read(reader, &p.GetStorage()));
+ return p;
+ }
+}
+
+class MOZ_STACK_CLASS MessageBufferWriter {
+ public:
+ // Create a MessageBufferWriter to write `full_len` bytes into `writer`.
+ // If the length exceeds a threshold, a shared memory region may be used
+ // instead of including the data inline.
+ //
+ // NOTE: This does _NOT_ write out the length of the buffer.
+ // NOTE: Data written this way _MUST_ be read using `MessageBufferReader`.
+ MessageBufferWriter(MessageWriter* writer, uint32_t full_len);
+ ~MessageBufferWriter();
+
+ MessageBufferWriter(const MessageBufferWriter&) = delete;
+ MessageBufferWriter& operator=(const MessageBufferWriter&) = delete;
+
+ // Write `len` bytes from `data` into the message.
+ //
+ // Exactly `full_len` bytes should be written across multiple calls before the
+ // `MessageBufferWriter` is destroyed.
+ //
+ // WARNING: all writes (other than the last write) must be multiples of 4
+ // bytes in length. Not doing this will lead to padding being introduced into
+ // the payload and break things. This can probably be improved in the future
+ // with deeper integration between `MessageBufferWriter` and `Pickle`.
+ bool WriteBytes(const void* data, uint32_t len);
+
+ private:
+ MessageWriter* writer_;
+ RefPtr<mozilla::ipc::SharedMemory> shmem_;
+ char* buffer_ = nullptr;
+ uint32_t remaining_ = 0;
+};
+
+class MOZ_STACK_CLASS MessageBufferReader {
+ public:
+ // Create a MessageBufferReader to read `full_len` bytes from `reader` which
+ // were written using `MessageBufferWriter`.
+ //
+ // NOTE: This may consume a shared memory region from the message, meaning
+ // that the same data cannot be read multiple times.
+ // NOTE: Data read this way _MUST_ be written using `MessageBufferWriter`.
+ MessageBufferReader(MessageReader* reader, uint32_t full_len);
+ ~MessageBufferReader();
+
+ MessageBufferReader(const MessageBufferReader&) = delete;
+ MessageBufferReader& operator=(const MessageBufferReader&) = delete;
+
+ // Read `count` bytes from the message into `data`.
+ //
+ // Exactly `full_len` bytes should be read across multiple calls before the
+ // `MessageBufferReader` is destroyed.
+ //
+ // WARNING: all reads (other than the last read) must be multiples of 4 bytes
+ // in length. Not doing this will lead to bytes being skipped in the payload
+ // and break things. This can probably be improved in the future with deeper
+ // integration between `MessageBufferReader` and `Pickle`.
+ [[nodiscard]] bool ReadBytesInto(void* data, uint32_t len);
+
+ private:
+ MessageReader* reader_;
+ RefPtr<mozilla::ipc::SharedMemory> shmem_;
+ const char* buffer_ = nullptr;
+ uint32_t remaining_ = 0;
+};
+
+// Whether or not it is safe to serialize the given type using
+// `WriteBytesOrShmem`.
+template <typename P>
+constexpr bool kUseWriteBytes =
+ !std::is_same_v<std::remove_const_t<std::remove_reference_t<P>>, bool> &&
+ (std::is_integral_v<std::remove_const_t<std::remove_reference_t<P>>> ||
+ std::is_floating_point_v<std::remove_const_t<std::remove_reference_t<P>>>);
+
+/**
+ * Helper for writing a contiguous sequence (such as for a string or array) into
+ * a message, with optimizations for basic integral and floating point types.
+ *
+ * Integral types will be copied into shared memory if the sequence exceeds 64k
+ * bytes in size.
+ *
+ * Values written with this method must be read with `ReadSequenceParam`.
+ *
+ * The type parameter specifies the semantics to use, and should generally
+ * either be `P&&` or `const P&`. The constness of the `data` argument should
+ * match this parameter.
+ */
+template <typename P>
+void WriteSequenceParam(MessageWriter* writer, std::remove_reference_t<P>* data,
+ size_t length) {
+ mozilla::CheckedUint32 ipc_length(length);
+ if (!ipc_length.isValid()) {
+ writer->FatalError("invalid length passed to WriteSequenceParam");
+ return;
+ }
+ writer->WriteUInt32(ipc_length.value());
+
+ if constexpr (kUseWriteBytes<P>) {
+ mozilla::CheckedUint32 byte_length =
+ ipc_length * sizeof(std::remove_reference_t<P>);
+ if (!byte_length.isValid()) {
+ writer->FatalError("invalid byte length in WriteSequenceParam");
+ return;
+ }
+ MessageBufferWriter buf_writer(writer, byte_length.value());
+ buf_writer.WriteBytes(data, byte_length.value());
+ } else {
+ auto* end = data + length;
+ for (auto* it = data; it != end; ++it) {
+ WriteParam(writer, std::forward<P>(*it));
+ }
+ }
+}
+
+template <typename P>
+bool ReadSequenceParamImpl(MessageReader* reader, P* data, uint32_t length) {
+ if (length == 0) {
+ return true;
+ }
+ if (!data) {
+ reader->FatalError("allocation failed in ReadSequenceParam");
+ return false;
+ }
+
+ if constexpr (kUseWriteBytes<P>) {
+ mozilla::CheckedUint32 byte_length(length);
+ byte_length *= sizeof(P);
+ if (!byte_length.isValid()) {
+ reader->FatalError("invalid byte length in ReadSequenceParam");
+ return false;
+ }
+ MessageBufferReader buf_reader(reader, byte_length.value());
+ return buf_reader.ReadBytesInto(data, byte_length.value());
+ } else {
+ P* end = data + length;
+ for (auto* it = data; it != end; ++it) {
+ if (!ReadParam(reader, it)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
+
+template <typename P, typename I>
+bool ReadSequenceParamImpl(MessageReader* reader, mozilla::Maybe<I>&& data,
+ uint32_t length) {
+ static_assert(!kUseWriteBytes<P>,
+ "Cannot return an output iterator if !kUseWriteBytes<P>");
+ static_assert(
+ std::is_base_of_v<std::output_iterator_tag,
+ typename std::iterator_traits<I>::iterator_category>,
+ "must be Maybe<output iterator>");
+ if (length == 0) {
+ return true;
+ }
+ if (!data) {
+ reader->FatalError("allocation failed in ReadSequenceParam");
+ return false;
+ }
+
+ for (uint32_t i = 0; i < length; ++i) {
+ auto elt = ReadParam<P>(reader);
+ if (!elt) {
+ return false;
+ }
+ *data.ref() = std::move(*elt);
+ ++data.ref();
+ }
+ return true;
+}
+
+/**
+ * Helper for reading a contiguous sequence (such as a string or array) into a
+ * message which was previously written using `WriteSequenceParam`.
+ *
+ * The function argument `allocator` will be called with the length of the
+ * sequence, and must return either a pointer to the memory region which the
+ * sequence should be read into, or a Maybe of a C++ output iterator which will
+ * infallibly accept length elements, and append them to the output sequence.
+ *
+ * If the type satisfies kUseWriteBytes, output iterators are not supported.
+ */
+template <typename P, typename F>
+bool WARN_UNUSED_RESULT ReadSequenceParam(MessageReader* reader,
+ F&& allocator) {
+ uint32_t length = 0;
+ if (!reader->ReadUInt32(&length)) {
+ reader->FatalError("failed to read byte length in ReadSequenceParam");
+ return false;
+ }
+
+ return ReadSequenceParamImpl<P>(reader, allocator(length), length);
+}
+
+// Temporary fallback class to allow types to declare serialization using the
+// IPDLParamTraits type class. Will be removed once all remaining
+// IPDLParamTraits implementations are gone. (bug 1754009)
+
+template <class P>
+struct ParamTraitsIPDLFallback {
+ template <class R>
+ static auto Write(MessageWriter* writer, R&& p)
+ -> decltype(mozilla::ipc::IPDLParamTraits<P>::Write(writer,
+ writer->GetActor(),
+ std::forward<R>(p))) {
+ mozilla::ipc::IPDLParamTraits<P>::Write(writer, writer->GetActor(),
+ std::forward<R>(p));
+ }
+ template <class R>
+ static auto Read(MessageReader* reader, R* r)
+ -> decltype(mozilla::ipc::IPDLParamTraits<P>::Read(reader,
+ reader->GetActor(),
+ r)) {
+ return mozilla::ipc::IPDLParamTraits<P>::Read(reader, reader->GetActor(),
+ r);
+ }
+};
+
+// Fundamental types.
+
+template <class P>
+struct ParamTraitsFundamental : ParamTraitsIPDLFallback<P> {};
+
+template <>
+struct ParamTraitsFundamental<bool> {
+ typedef bool param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteBool(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadBool(r);
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<int> {
+ typedef int param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteInt(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadInt(r);
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<long> {
+ typedef long param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteLong(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadLong(r);
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<unsigned long> {
+ typedef unsigned long param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteULong(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadULong(r);
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<long long> {
+ typedef long long param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteBytes(&p, sizeof(param_type));
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadBytesInto(r, sizeof(*r));
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<unsigned long long> {
+ typedef unsigned long long param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteBytes(&p, sizeof(param_type));
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadBytesInto(r, sizeof(*r));
+ }
+};
+
+template <>
+struct ParamTraitsFundamental<double> {
+ typedef double param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteDouble(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadDouble(r);
+ }
+};
+
+// Fixed-size <stdint.h> types.
+
+template <class P>
+struct ParamTraitsFixed : ParamTraitsFundamental<P> {};
+
+template <>
+struct ParamTraitsFixed<int16_t> {
+ typedef int16_t param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteInt16(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadInt16(r);
+ }
+};
+
+template <>
+struct ParamTraitsFixed<uint16_t> {
+ typedef uint16_t param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteUInt16(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadUInt16(r);
+ }
+};
+
+template <>
+struct ParamTraitsFixed<uint32_t> {
+ typedef uint32_t param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteUInt32(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadUInt32(r);
+ }
+};
+
+template <>
+struct ParamTraitsFixed<int64_t> {
+ typedef int64_t param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteInt64(p);
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadInt64(r);
+ }
+};
+
+template <>
+struct ParamTraitsFixed<uint64_t> {
+ typedef uint64_t param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteInt64(static_cast<int64_t>(p));
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadInt64(reinterpret_cast<int64_t*>(r));
+ }
+};
+
+// std::* types.
+
+template <class P>
+struct ParamTraitsStd : ParamTraitsFixed<P> {};
+
+template <class T>
+struct ParamTraitsStd<std::basic_string<T>> {
+ typedef std::basic_string<T> param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ WriteSequenceParam<const T&>(writer, p.data(), p.size());
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return ReadSequenceParam<T>(reader, [&](uint32_t length) -> T* {
+ r->resize(length);
+ return r->data();
+ });
+ }
+};
+
+template <class K, class V>
+struct ParamTraitsStd<std::map<K, V>> {
+ typedef std::map<K, V> param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ WriteParam(writer, static_cast<int>(p.size()));
+ typename param_type::const_iterator iter;
+ for (iter = p.begin(); iter != p.end(); ++iter) {
+ WriteParam(writer, iter->first);
+ WriteParam(writer, iter->second);
+ }
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ int size;
+ if (!ReadParam(reader, &size) || size < 0) return false;
+ for (int i = 0; i < size; ++i) {
+ K k;
+ if (!ReadParam(reader, &k)) return false;
+ V& value = (*r)[k];
+ if (!ReadParam(reader, &value)) return false;
+ }
+ return true;
+ }
+};
+
+// Windows-specific types.
+
+template <class P>
+struct ParamTraitsWindows : ParamTraitsStd<P> {};
+
+#if defined(OS_WIN)
+template <>
+struct ParamTraitsWindows<HANDLE> {
+ static_assert(sizeof(HANDLE) == sizeof(intptr_t), "Wrong size for HANDLE?");
+
+ static void Write(MessageWriter* writer, HANDLE p) {
+ writer->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(MessageReader* reader, HANDLE* r) {
+ return reader->ReadIntPtr(reinterpret_cast<intptr_t*>(r));
+ }
+};
+
+template <>
+struct ParamTraitsWindows<HWND> {
+ static_assert(sizeof(HWND) == sizeof(intptr_t), "Wrong size for HWND?");
+
+ static void Write(MessageWriter* writer, HWND p) {
+ writer->WriteIntPtr(reinterpret_cast<intptr_t>(p));
+ }
+ static bool Read(MessageReader* reader, HWND* r) {
+ return reader->ReadIntPtr(reinterpret_cast<intptr_t*>(r));
+ }
+};
+#endif // defined(OS_WIN)
+
+// Various ipc/chromium types.
+
+template <class P>
+struct ParamTraitsIPC : ParamTraitsWindows<P> {};
+
+// `UniqueFileHandle` may be serialized over IPC channels. On the receiving
+// side, the UniqueFileHandle is a valid duplicate of the handle which was
+// transmitted.
+//
+// When sending a UniqueFileHandle, the handle must be valid at the time of
+// transmission. As transmission is asynchronous, this requires passing
+// ownership of the handle to IPC.
+//
+// A UniqueFileHandle may only be read once. After it has been read once, it
+// will be consumed, and future reads will return an invalid handle.
+template <>
+struct ParamTraitsIPC<mozilla::UniqueFileHandle> {
+ typedef mozilla::UniqueFileHandle param_type;
+ static void Write(MessageWriter* writer, param_type&& p) {
+ const bool valid = p != nullptr;
+ WriteParam(writer, valid);
+ if (valid) {
+ if (!writer->WriteFileHandle(std::move(p))) {
+ writer->FatalError("Too many file handles for one message!");
+ NOTREACHED() << "Too many file handles for one message!";
+ }
+ }
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ bool valid;
+ if (!ReadParam(reader, &valid)) {
+ reader->FatalError("Error reading file handle validity");
+ return false;
+ }
+
+ if (!valid) {
+ *r = nullptr;
+ return true;
+ }
+
+ if (!reader->ConsumeFileHandle(r)) {
+ reader->FatalError("File handle not found in message!");
+ return false;
+ }
+ return true;
+ }
+};
+
+#if defined(OS_MACOSX) || defined(OS_IOS)
+// `UniqueMachSendRight` may be serialized over IPC channels. On the receiving
+// side, the UniqueMachSendRight is the local name of the right which was
+// transmitted.
+//
+// When sending a UniqueMachSendRight, the right must be valid at the time of
+// transmission. As transmission is asynchronous, this requires passing
+// ownership of the handle to IPC.
+//
+// A UniqueMachSendRight may only be read once. After it has been read once, it
+// will be consumed, and future reads will return an invalid right.
+template <>
+struct ParamTraitsIPC<mozilla::UniqueMachSendRight> {
+ typedef mozilla::UniqueMachSendRight param_type;
+ static void Write(MessageWriter* writer, param_type&& p) {
+ const bool valid = p != nullptr;
+ WriteParam(writer, valid);
+ if (valid) {
+ if (!writer->WriteMachSendRight(std::move(p))) {
+ writer->FatalError("Too many mach send rights for one message!");
+ NOTREACHED() << "Too many mach send rights for one message!";
+ }
+ }
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ bool valid;
+ if (!ReadParam(reader, &valid)) {
+ reader->FatalError("Error reading mach send right validity");
+ return false;
+ }
+
+ if (!valid) {
+ *r = nullptr;
+ return true;
+ }
+
+ if (!reader->ConsumeMachSendRight(r)) {
+ reader->FatalError("Mach send right not found in message!");
+ return false;
+ }
+ return true;
+ }
+};
+#endif
+
+// Mozilla-specific types.
+
+template <class P>
+struct ParamTraitsMozilla : ParamTraitsIPC<P> {};
+
+// Sending-only specialization for mozilla::Span<T const>. Uses an identical
+// serialization format as `const nsTArray<T>&`.
+template <class T>
+struct ParamTraitsMozilla<mozilla::Span<const T>> {
+ static void Write(MessageWriter* writer, mozilla::Span<const T> p) {
+ WriteSequenceParam<const T>(writer, p.Elements(), p.Length());
+ }
+};
+
+template <>
+struct ParamTraitsMozilla<nsresult> {
+ typedef nsresult param_type;
+ static void Write(MessageWriter* writer, const param_type& p) {
+ writer->WriteUInt32(static_cast<uint32_t>(p));
+ }
+ static bool Read(MessageReader* reader, param_type* r) {
+ return reader->ReadUInt32(reinterpret_cast<uint32_t*>(r));
+ }
+};
+
+// See comments for the IPDLParamTraits specializations for RefPtr<T> and
+// nsCOMPtr<T> for more details.
+template <class T>
+struct ParamTraitsMozilla<RefPtr<T>> {
+ static void Write(MessageWriter* writer, const RefPtr<T>& p) {
+ ParamTraits<T*>::Write(writer, p.get());
+ }
+
+ static bool Read(MessageReader* reader, RefPtr<T>* r) {
+ return ParamTraits<T*>::Read(reader, r);
+ }
+};
+
+template <class T>
+struct ParamTraitsMozilla<nsCOMPtr<T>> {
+ static void Write(MessageWriter* writer, const nsCOMPtr<T>& p) {
+ ParamTraits<T*>::Write(writer, p.get());
+ }
+
+ static bool Read(MessageReader* reader, nsCOMPtr<T>* r) {
+ RefPtr<T> refptr;
+ if (!ParamTraits<T*>::Read(reader, &refptr)) {
+ return false;
+ }
+ *r = std::move(refptr);
+ return true;
+ }
+};
+
+template <class T>
+struct ParamTraitsMozilla<mozilla::NotNull<T>> {
+ static void Write(MessageWriter* writer, const mozilla::NotNull<T>& p) {
+ ParamTraits<T>::Write(writer, p.get());
+ }
+
+ static ReadResult<mozilla::NotNull<T>> Read(MessageReader* reader) {
+ auto ptr = ReadParam<T>(reader);
+ if (!ptr) {
+ return {};
+ }
+ if (!*ptr) {
+ reader->FatalError("unexpected null value");
+ return {};
+ }
+ return mozilla::WrapNotNull(std::move(*ptr));
+ }
+};
+
+// Finally, ParamTraits itself.
+
+template <class P>
+struct ParamTraits : ParamTraitsMozilla<P> {};
+
+} // namespace IPC
+
+#endif // CHROME_COMMON_IPC_MESSAGE_UTILS_H_
diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.cc b/ipc/chromium/src/chrome/common/mach_ipc_mac.cc
new file mode 100644
index 0000000000..2fa1a47fd3
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.cc
@@ -0,0 +1,93 @@
+// Copyright (c) 2006-2008 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 "chrome/common/mach_ipc_mac.h"
+
+#include "base/logging.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "nsDebug.h"
+
+namespace {
+// Struct for sending a Mach message with a single port.
+struct MachSinglePortMessage {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t data;
+};
+
+// Struct for receiving a Mach message with a single port.
+struct MachSinglePortMessageTrailer : MachSinglePortMessage {
+ mach_msg_audit_trailer_t trailer;
+};
+} // namespace
+
+//==============================================================================
+kern_return_t MachSendPortSendRight(
+ mach_port_t endpoint, mach_port_t attachment,
+ mozilla::Maybe<mach_msg_timeout_t> opt_timeout,
+ mach_msg_type_name_t endpoint_disposition) {
+ mach_msg_option_t opts = MACH_SEND_MSG;
+ mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE;
+ if (opt_timeout) {
+ opts |= MACH_SEND_TIMEOUT;
+ timeout = *opt_timeout;
+ }
+
+ MachSinglePortMessage send_msg{};
+ send_msg.header.msgh_bits =
+ MACH_MSGH_BITS(endpoint_disposition, 0) | MACH_MSGH_BITS_COMPLEX;
+ send_msg.header.msgh_size = sizeof(send_msg);
+ send_msg.header.msgh_remote_port = endpoint;
+ send_msg.header.msgh_local_port = MACH_PORT_NULL;
+ send_msg.header.msgh_reserved = 0;
+ send_msg.header.msgh_id = 0;
+ send_msg.body.msgh_descriptor_count = 1;
+ send_msg.data.name = attachment;
+ send_msg.data.disposition = MACH_MSG_TYPE_COPY_SEND;
+ send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR;
+
+ return mach_msg(&send_msg.header, opts, send_msg.header.msgh_size, 0,
+ MACH_PORT_NULL, timeout, MACH_PORT_NULL);
+}
+
+//==============================================================================
+kern_return_t MachReceivePortSendRight(
+ const mozilla::UniqueMachReceiveRight& endpoint,
+ mozilla::Maybe<mach_msg_timeout_t> opt_timeout,
+ mozilla::UniqueMachSendRight* attachment, audit_token_t* audit_token) {
+ mach_msg_option_t opts = MACH_RCV_MSG |
+ MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0) |
+ MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT);
+ mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE;
+ if (opt_timeout) {
+ opts |= MACH_RCV_TIMEOUT;
+ timeout = *opt_timeout;
+ }
+
+ MachSinglePortMessageTrailer recv_msg{};
+ recv_msg.header.msgh_local_port = endpoint.get();
+ recv_msg.header.msgh_size = sizeof(recv_msg);
+
+ kern_return_t kr =
+ mach_msg(&recv_msg.header, opts, 0, recv_msg.header.msgh_size,
+ endpoint.get(), timeout, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS) {
+ return kr;
+ }
+
+ if (NS_WARN_IF(!(recv_msg.header.msgh_bits & MACH_MSGH_BITS_COMPLEX)) ||
+ NS_WARN_IF(recv_msg.body.msgh_descriptor_count != 1) ||
+ NS_WARN_IF(recv_msg.data.type != MACH_MSG_PORT_DESCRIPTOR) ||
+ NS_WARN_IF(recv_msg.data.disposition != MACH_MSG_TYPE_MOVE_SEND) ||
+ NS_WARN_IF(recv_msg.header.msgh_size != sizeof(MachSinglePortMessage))) {
+ mach_msg_destroy(&recv_msg.header);
+ return KERN_FAILURE; // Invalid message format
+ }
+
+ attachment->reset(recv_msg.data.name);
+ if (audit_token) {
+ *audit_token = recv_msg.trailer.msgh_audit;
+ }
+ return KERN_SUCCESS;
+}
diff --git a/ipc/chromium/src/chrome/common/mach_ipc_mac.h b/ipc/chromium/src/chrome/common/mach_ipc_mac.h
new file mode 100644
index 0000000000..c9af70c0c6
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_ipc_mac.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef BASE_MACH_IPC_MAC_H_
+#define BASE_MACH_IPC_MAC_H_
+
+#include <mach/mach.h>
+#include <mach/message.h>
+#include <servers/bootstrap.h>
+#include <sys/types.h>
+
+#include "mozilla/Maybe.h"
+#include "mozilla/Result.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+//==============================================================================
+// Helper function for sending a minimal mach IPC messages with a single send
+// right attached. The endpoint will not be consumed unless the
+// `endpoint_disposition` argument is set to a consuming disposition, and
+// `KERN_SUCCESS` is returned.
+kern_return_t MachSendPortSendRight(
+ mach_port_t endpoint, mach_port_t attachment,
+ mozilla::Maybe<mach_msg_timeout_t> opt_timeout,
+ mach_msg_type_name_t endpoint_disposition = MACH_MSG_TYPE_COPY_SEND);
+
+//==============================================================================
+// Helper function for receiving a minimal mach IPC message with a single send
+// right attached.
+// If the `audit_token` parameter is provided, it will be populated with the
+// sender's audit token, which can be used to verify the identity of the sender.
+kern_return_t MachReceivePortSendRight(
+ const mozilla::UniqueMachReceiveRight& endpoint,
+ mozilla::Maybe<mach_msg_timeout_t> opt_timeout,
+ mozilla::UniqueMachSendRight* attachment,
+ audit_token_t* audit_token = nullptr);
+
+#endif // BASE_MACH_IPC_MAC_H_
diff --git a/ipc/chromium/src/chrome/common/mach_message_source_mac.cc b/ipc/chromium/src/chrome/common/mach_message_source_mac.cc
new file mode 100644
index 0000000000..d3be7efdb5
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_message_source_mac.cc
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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 "chrome/common/mach_message_source_mac.h"
+
+#include "base/logging.h"
+
+MachMessageSource::MachMessageSource(mach_port_t port,
+ MachPortListener* msg_listener,
+ bool* success) {
+ DCHECK(msg_listener);
+ DCHECK(success);
+ DCHECK(port != MACH_PORT_NULL);
+
+ CFMachPortContext port_context = {0};
+ port_context.info = msg_listener;
+
+ scoped_cftyperef<CFMachPortRef> cf_mach_port_ref(CFMachPortCreateWithPort(
+ kCFAllocatorDefault, port, MachMessageSource::OnReceiveMachMessage,
+ &port_context, NULL));
+
+ if (cf_mach_port_ref.get() == NULL) {
+ CHROMIUM_LOG(WARNING) << "CFMachPortCreate failed";
+ *success = false;
+ return;
+ }
+
+ // Create a RL source.
+ machport_runloop_ref_.reset(CFMachPortCreateRunLoopSource(
+ kCFAllocatorDefault, cf_mach_port_ref.get(), 0));
+
+ if (machport_runloop_ref_.get() == NULL) {
+ CHROMIUM_LOG(WARNING) << "CFMachPortCreateRunLoopSource failed";
+ *success = false;
+ return;
+ }
+
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), machport_runloop_ref_.get(),
+ kCFRunLoopCommonModes);
+ *success = true;
+}
+
+MachMessageSource::~MachMessageSource() {
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), machport_runloop_ref_.get(),
+ kCFRunLoopCommonModes);
+}
+
+// static
+void MachMessageSource::OnReceiveMachMessage(CFMachPortRef port, void* msg,
+ CFIndex size, void* closure) {
+ MachPortListener* msg_listener = static_cast<MachPortListener*>(closure);
+ size_t msg_size = (size < 0) ? 0 : static_cast<size_t>(size);
+ DCHECK(msg && msg_size > 0); // this should never happen!
+
+ if (msg_listener && msg && msg_size > 0) {
+ msg_listener->OnMachMessageReceived(msg, msg_size);
+ }
+}
diff --git a/ipc/chromium/src/chrome/common/mach_message_source_mac.h b/ipc/chromium/src/chrome/common/mach_message_source_mac.h
new file mode 100644
index 0000000000..92ce0a73d4
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/mach_message_source_mac.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2008 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.
+
+#ifndef CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_
+#define CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_
+
+#include <CoreServices/CoreServices.h>
+
+#include "base/scoped_cftyperef.h"
+
+// Handles registering and cleaning up after a CFRunloopSource for a Mach port.
+// Messages received on the port are piped through to a delegate.
+//
+// Example:
+// class MyListener : public MachMessageSource::MachPortListener {
+// public:
+// void OnMachMessageReceived(void* mach_msg, size_t size) {
+// printf("received message on Mach port\n");
+// }
+// };
+//
+// mach_port_t a_port = ...;
+// MyListener listener;
+// bool success = false;
+// MachMessageSource message_source(port, listener, &success);
+//
+// if (!success) {
+// exit(1); // Couldn't register mach runloop source.
+// }
+//
+// CFRunLoopRun(); // Process messages on runloop...
+class MachMessageSource {
+ public:
+ // Classes that want to listen on a Mach port can implement
+ // OnMachMessageReceived, |mach_msg| is a pointer to the raw message data and
+ // |size| is the buffer size;
+ class MachPortListener {
+ public:
+ virtual void OnMachMessageReceived(void* mach_msg, size_t size) = 0;
+ };
+
+ // |listener| is a week reference passed to CF, it needs to remain in
+ // existence till this object is destroeyd.
+ MachMessageSource(mach_port_t port, MachPortListener* listener,
+ bool* success);
+ ~MachMessageSource();
+
+ private:
+ // Called by CF when a new message arrives on the Mach port.
+ static void OnReceiveMachMessage(CFMachPortRef port, void* msg, CFIndex size,
+ void* closure);
+
+ scoped_cftyperef<CFRunLoopSourceRef> machport_runloop_ref_;
+ DISALLOW_COPY_AND_ASSIGN(MachMessageSource);
+};
+
+#endif // CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_
diff --git a/ipc/chromium/src/chrome/common/process_watcher.h b/ipc/chromium/src/chrome/common/process_watcher.h
new file mode 100644
index 0000000000..f1ccae6310
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/process_watcher.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_COMMON_PROCESS_WATCHER_H_
+#define CHROME_COMMON_PROCESS_WATCHER_H_
+
+#include "base/basictypes.h"
+#include "base/process_util.h"
+
+class ProcessWatcher {
+ public:
+ // This method ensures that the specified process eventually terminates, and
+ // then it closes the given process handle.
+ //
+ // It assumes that the process has already been signalled to exit, and it
+ // begins by waiting a small amount of time for it to exit. If the process
+ // does not appear to have exited, then this function starts to become
+ // aggressive about ensuring that the process terminates.
+ //
+ // This method does not block the calling thread.
+ //
+ // NOTE: The process handle must have been opened with the PROCESS_TERMINATE
+ // and SYNCHRONIZE permissions.
+ //
+ static void EnsureProcessTerminated(base::ProcessHandle process_handle,
+ bool force = true);
+
+ private:
+ // Do not instantiate this class.
+ ProcessWatcher();
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessWatcher);
+};
+
+#endif // CHROME_COMMON_PROCESS_WATCHER_H_
diff --git a/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc b/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc
new file mode 100644
index 0000000000..8f62dfdbda
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <time.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "prenv.h"
+
+#include "chrome/common/process_watcher.h"
+
+// Maximum amount of time (in milliseconds) to wait for the process to exit.
+// XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc
+static constexpr int kMaxWaitMs = 2000;
+
+// This is also somewhat arbitrary, but loosely based on Try results.
+// See also toolkit.asyncshutdown.crash_timeout (currently 60s) after
+// which the parent process will be killed.
+#ifdef MOZ_CODE_COVERAGE
+// Code coverage instrumentation can be slow (especially when writing
+// out data, which has to take a lock on the data files).
+static constexpr int kShutdownWaitMs = 80000;
+#elif defined(MOZ_ASAN) || defined(MOZ_TSAN)
+// Sanitizers slow things down in some cases; see bug 1806224.
+static constexpr int kShutdownWaitMs = 40000;
+#else
+static constexpr int kShutdownWaitMs = 8000;
+#endif
+
+namespace {
+
+class ChildReaper : public base::MessagePumpLibevent::SignalEvent,
+ public base::MessagePumpLibevent::SignalWatcher {
+ public:
+ explicit ChildReaper(pid_t process) : process_(process) {}
+
+ virtual ~ChildReaper() {
+ // subclasses should have cleaned up |process_| already
+ DCHECK(!process_);
+
+ // StopCatching() is implicit
+ }
+
+ virtual void OnSignal(int sig) override {
+ DCHECK(SIGCHLD == sig);
+ DCHECK(process_);
+
+ // this may be the SIGCHLD for a process other than |process_|
+ if (base::IsProcessDead(process_)) {
+ process_ = 0;
+ StopCatching();
+ }
+ }
+
+ protected:
+ void WaitForChildExit() {
+ CHECK(process_);
+ while (!base::IsProcessDead(process_, true)) {
+ // It doesn't matter if this is interrupted; we just need to
+ // wait for some amount of time while the other process status
+ // event is (hopefully) handled. This is used only during an
+ // error case at shutdown, so a 1s wait won't be too noticeable.
+ sleep(1);
+ }
+ }
+
+ pid_t process_;
+
+ private:
+ ChildReaper(const ChildReaper&) = delete;
+
+ const ChildReaper& operator=(const ChildReaper&) = delete;
+};
+
+// Fear the reaper
+class ChildGrimReaper : public ChildReaper, public mozilla::Runnable {
+ public:
+ explicit ChildGrimReaper(pid_t process)
+ : ChildReaper(process), mozilla::Runnable("ChildGrimReaper") {}
+
+ virtual ~ChildGrimReaper() {
+ if (process_) KillProcess();
+ }
+
+ NS_IMETHOD Run() override {
+ // we may have already been signaled by the time this runs
+ if (process_) KillProcess();
+
+ return NS_OK;
+ }
+
+ private:
+ void KillProcess() {
+ DCHECK(process_);
+
+ if (base::IsProcessDead(process_)) {
+ process_ = 0;
+ return;
+ }
+
+ if (0 == kill(process_, SIGKILL)) {
+ // XXX this will block for whatever amount of time it takes the
+ // XXX OS to tear down the process's resources. might need to
+ // XXX rethink this if it proves expensive
+ WaitForChildExit();
+ } else {
+ CHROMIUM_LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!"
+ << "(" << errno << ").";
+ }
+ process_ = 0;
+ }
+
+ ChildGrimReaper(const ChildGrimReaper&) = delete;
+
+ const ChildGrimReaper& operator=(const ChildGrimReaper&) = delete;
+};
+
+class ChildLaxReaper : public ChildReaper,
+ public MessageLoop::DestructionObserver {
+ public:
+ explicit ChildLaxReaper(pid_t process) : ChildReaper(process) {}
+
+ virtual ~ChildLaxReaper() {
+ // WillDestroyCurrentMessageLoop() should have reaped process_ already
+ DCHECK(!process_);
+ }
+
+ virtual void OnSignal(int sig) override {
+ ChildReaper::OnSignal(sig);
+
+ if (!process_) {
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+ }
+
+ virtual void WillDestroyCurrentMessageLoop() override {
+ DCHECK(process_);
+ if (!process_) {
+ return;
+ }
+
+ // Exception for the fake hang tests in ipc/glue/test/browser
+ if (!PR_GetEnv("MOZ_TEST_CHILD_EXIT_HANG")) {
+ CrashProcessIfHanging();
+ }
+ if (process_) {
+ WaitForChildExit();
+ process_ = 0;
+ }
+
+ // XXX don't think this is necessary, since destruction can only
+ // be observed once, but can't hurt
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+
+ private:
+ ChildLaxReaper(const ChildLaxReaper&) = delete;
+
+ void CrashProcessIfHanging() {
+ if (base::IsProcessDead(process_)) {
+ process_ = 0;
+ return;
+ }
+
+ // If child processes seems to be hanging on shutdown, wait for a
+ // reasonable time. The wait is global instead of per-process
+ // because the child processes should be shutting down in
+ // parallel, and also we're potentially racing global timeouts
+ // like nsTerminator. (The counter doesn't need to be atomic;
+ // this is always called on the I/O thread.)
+ static int sWaitMs = kShutdownWaitMs;
+ if (sWaitMs > 0) {
+ CHROMIUM_LOG(WARNING)
+ << "Process " << process_
+ << " may be hanging at shutdown; will wait for up to " << sWaitMs
+ << "ms";
+ }
+ // There isn't a way to do a time-limited wait that's both
+ // portable and doesn't require messing with signals. Instead, we
+ // sleep in short increments and poll the process status.
+ while (sWaitMs > 0) {
+ static constexpr int kWaitTickMs = 200;
+ struct timespec ts = {kWaitTickMs / 1000, (kWaitTickMs % 1000) * 1000000};
+ HANDLE_EINTR(nanosleep(&ts, &ts));
+ sWaitMs -= kWaitTickMs;
+
+ if (base::IsProcessDead(process_)) {
+ process_ = 0;
+ return;
+ }
+ }
+
+ // We want TreeHerder to flag this log line as an error, so that
+ // this is more obviously a deliberate crash; "fatal error" is one
+ // of the strings it looks for.
+ CHROMIUM_LOG(ERROR)
+ << "Process " << process_
+ << " hanging at shutdown; attempting crash report (fatal error).";
+
+ kill(process_, SIGABRT);
+ }
+
+ const ChildLaxReaper& operator=(const ChildLaxReaper&) = delete;
+};
+
+} // namespace
+
+/**
+ * Do everything possible to ensure that |process| has been reaped
+ * before this process exits.
+ *
+ * |grim| decides how strict to be with the child's shutdown.
+ *
+ * | child exit timeout | upon parent shutdown:
+ * +--------------------+----------------------------------
+ * force=true | 2 seconds | kill(child, SIGKILL)
+ * force=false | infinite | waitpid(child)
+ *
+ * If a child process doesn't shut down properly, and |grim=false|
+ * used, then the parent will wait on the child forever. So,
+ * |force=false| is expected to be used when an external entity can be
+ * responsible for terminating hung processes, e.g. automated test
+ * harnesses.
+ */
+void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process,
+ bool force) {
+ DCHECK(process != base::GetCurrentProcId());
+ DCHECK(process > 0);
+
+ if (base::IsProcessDead(process)) return;
+
+ MessageLoopForIO* loop = MessageLoopForIO::current();
+ if (force) {
+ RefPtr<ChildGrimReaper> reaper = new ChildGrimReaper(process);
+
+ loop->CatchSignal(SIGCHLD, reaper, reaper);
+ // |loop| takes ownership of |reaper|
+ loop->PostDelayedTask(reaper.forget(), kMaxWaitMs);
+ } else {
+ ChildLaxReaper* reaper = new ChildLaxReaper(process);
+
+ loop->CatchSignal(SIGCHLD, reaper, reaper);
+ // |reaper| destroys itself after destruction notification
+ loop->AddDestructionObserver(reaper);
+ }
+}
diff --git a/ipc/chromium/src/chrome/common/process_watcher_win.cc b/ipc/chromium/src/chrome/common/process_watcher_win.cc
new file mode 100644
index 0000000000..2e0efbf6a7
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/process_watcher_win.cc
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 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 "chrome/common/process_watcher.h"
+
+#include <algorithm>
+#include <processthreadsapi.h>
+#include <synchapi.h>
+#include "base/message_loop.h"
+#include "base/object_watcher.h"
+#include "prenv.h"
+
+// Maximum amount of time (in milliseconds) to wait for the process to exit.
+static constexpr int kWaitInterval = 2000;
+
+// This is somewhat arbitrary, but based on Try run results. When
+// changing this, be aware of toolkit.asyncshutdown.crash_timeout
+// (currently 60s), after which the parent process will be killed.
+#ifdef MOZ_CODE_COVERAGE
+// Child processes seem to take longer to shut down on ccov builds, at
+// least in the wdspec tests; ~20s has been observed, and we'll spam
+// false positives unless this is increased.
+static constexpr DWORD kShutdownWaitMs = 80000;
+#elif defined(MOZ_ASAN) || defined(MOZ_TSAN)
+// Sanitizers also slow things down in some cases; see bug 1806224.
+static constexpr DWORD kShutdownWaitMs = 40000;
+#else
+static constexpr DWORD kShutdownWaitMs = 8000;
+#endif
+
+namespace {
+
+static bool IsProcessDead(base::ProcessHandle process) {
+ return WaitForSingleObject(process, 0) == WAIT_OBJECT_0;
+}
+
+class ChildReaper : public mozilla::Runnable,
+ public base::ObjectWatcher::Delegate,
+ public MessageLoop::DestructionObserver {
+ public:
+ explicit ChildReaper(base::ProcessHandle process, bool force)
+ : mozilla::Runnable("ChildReaper"), process_(process), force_(force) {
+ watcher_.StartWatching(process_, this);
+ }
+
+ virtual ~ChildReaper() {
+ if (process_) {
+ KillProcess();
+ DCHECK(!process_) << "Make sure to close the handle.";
+ }
+ }
+
+ // MessageLoop::DestructionObserver -----------------------------------------
+
+ virtual void WillDestroyCurrentMessageLoop() {
+ MOZ_ASSERT(!force_);
+ if (process_) {
+ // Exception for the fake hang tests in ipc/glue/test/browser
+ if (!PR_GetEnv("MOZ_TEST_CHILD_EXIT_HANG")) {
+ CrashProcessIfHanging();
+ }
+ WaitForSingleObject(process_, INFINITE);
+ base::CloseProcessHandle(process_);
+ process_ = 0;
+
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+ }
+
+ // Task ---------------------------------------------------------------------
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(force_);
+ if (process_) {
+ KillProcess();
+ }
+ return NS_OK;
+ }
+
+ // MessageLoop::Watcher -----------------------------------------------------
+
+ virtual void OnObjectSignaled(HANDLE object) {
+ // When we're called from KillProcess, the ObjectWatcher may still be
+ // watching. the process handle, so make sure it has stopped.
+ watcher_.StopWatching();
+
+ base::CloseProcessHandle(process_);
+ process_ = 0;
+
+ if (!force_) {
+ MessageLoop::current()->RemoveDestructionObserver(this);
+ delete this;
+ }
+ }
+
+ private:
+ void KillProcess() {
+ MOZ_ASSERT(force_);
+
+ // OK, time to get frisky. We don't actually care when the process
+ // terminates. We just care that it eventually terminates, and that's what
+ // TerminateProcess should do for us. Don't check for the result code since
+ // it fails quite often. This should be investigated eventually.
+ TerminateProcess(process_, base::PROCESS_END_PROCESS_WAS_HUNG);
+
+ // Now, just cleanup as if the process exited normally.
+ OnObjectSignaled(process_);
+ }
+
+ void CrashProcessIfHanging() {
+ if (IsProcessDead(process_)) {
+ return;
+ }
+ DWORD pid = GetProcessId(process_);
+ DCHECK(pid != 0);
+
+ // If child processes seems to be hanging on shutdown, wait for a
+ // reasonable time. The wait is global instead of per-process
+ // because the child processes should be shutting down in
+ // parallel, and also we're potentially racing global timeouts
+ // like nsTerminator. (The counter doesn't need to be atomic;
+ // this is always called on the I/O thread.)
+ static DWORD sWaitMs = kShutdownWaitMs;
+ if (sWaitMs > 0) {
+ CHROMIUM_LOG(WARNING)
+ << "Process " << pid
+ << " may be hanging at shutdown; will wait for up to " << sWaitMs
+ << "ms";
+ }
+ const auto beforeWait = mozilla::TimeStamp::NowLoRes();
+ const DWORD waitStatus = WaitForSingleObject(process_, sWaitMs);
+
+ const double elapsed =
+ (mozilla::TimeStamp::NowLoRes() - beforeWait).ToMilliseconds();
+ sWaitMs -= static_cast<DWORD>(
+ std::clamp(elapsed, 0.0, static_cast<double>(sWaitMs)));
+
+ switch (waitStatus) {
+ case WAIT_TIMEOUT:
+ // The process is still running.
+ break;
+ case WAIT_OBJECT_0:
+ // The process exited.
+ return;
+ case WAIT_FAILED:
+ CHROMIUM_LOG(ERROR) << "Waiting for process " << pid
+ << " failed; error " << GetLastError();
+ DCHECK(false) << "WaitForSingleObject failed";
+ // Process status unclear; assume it's gone.
+ return;
+ default:
+ DCHECK(false) << "WaitForSingleObject returned " << waitStatus;
+ // Again, not clear what's happening so avoid touching the process
+ return;
+ }
+
+ // We want TreeHerder to flag this log line as an error, so that
+ // this is more obviously a deliberate crash; "fatal error" is one
+ // of the strings it looks for.
+ CHROMIUM_LOG(ERROR)
+ << "Process " << pid
+ << " hanging at shutdown; attempting crash report (fatal error)";
+
+ // We're going to use CreateRemoteThread to call DbgBreakPoint in
+ // the target process; it's in a "known DLL" so it should be at
+ // the same address in all processes. (Normal libraries, like
+ // xul.dll, are usually at the same address but can be relocated
+ // in case of conflict.)
+ //
+ // DbgBreakPoint doesn't take an argument, so we can give it an
+ // arbitrary value to make it easier to identify these crash
+ // reports. (reinterpret_cast isn't constexpr, so this is
+ // declared as an integer and cast to the required type later.)
+ // The primary use case for all of this is in CI, where we'll also
+ // have log messages, but if these crashes end up in Socorro in
+ // significant numbers then we'll be able to look for this value.
+ static constexpr uint64_t kIpcMagic = 0x43504900435049;
+
+ const HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
+ if (!ntdll) {
+ CHROMIUM_LOG(ERROR) << "couldn't find ntdll.dll: error "
+ << GetLastError();
+ return;
+ }
+ const auto dbgBreak = reinterpret_cast<LPTHREAD_START_ROUTINE>(
+ GetProcAddress(ntdll, "DbgBreakPoint"));
+ if (!dbgBreak) {
+ CHROMIUM_LOG(ERROR) << "couldn't find DbgBreakPoint: error "
+ << GetLastError();
+ return;
+ }
+
+ const DWORD rights = PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION |
+ PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
+ PROCESS_VM_READ;
+ HANDLE process_priv = nullptr;
+ if (!DuplicateHandle(GetCurrentProcess(), process_, GetCurrentProcess(),
+ &process_priv, rights, /* inherit */ FALSE,
+ /* options */ 0)) {
+ const auto error = GetLastError();
+ CHROMIUM_LOG(ERROR) << "OpenProcess: error " << error;
+ } else {
+ DCHECK(process_priv);
+ HANDLE thread =
+ CreateRemoteThread(process_priv, /* sec attr */ nullptr,
+ /* stack */ 0, dbgBreak, (LPVOID)kIpcMagic,
+ /* flags */ 0, nullptr);
+ if (!thread) {
+ const auto error = GetLastError();
+ CHROMIUM_LOG(ERROR) << "CreateRemoteThread: error " << error;
+ } else {
+ CloseHandle(thread);
+ }
+ CloseHandle(process_priv);
+ }
+ }
+
+ // The process that we are watching.
+ base::ProcessHandle process_;
+
+ base::ObjectWatcher watcher_;
+
+ bool force_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ChildReaper);
+};
+
+} // namespace
+
+// static
+void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process,
+ bool force) {
+ DCHECK(process != GetCurrentProcess());
+
+ // If already signaled, then we are done!
+ if (IsProcessDead(process)) {
+ base::CloseProcessHandle(process);
+ return;
+ }
+
+ MessageLoopForIO* loop = MessageLoopForIO::current();
+ if (force) {
+ RefPtr<mozilla::Runnable> task = new ChildReaper(process, force);
+ loop->PostDelayedTask(task.forget(), kWaitInterval);
+ } else {
+ loop->AddDestructionObserver(new ChildReaper(process, force));
+ }
+}