summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerikchen <erikchen@chromium.org>2015-10-06 12:59:39 -0700
committerCommit bot <commit-bot@chromium.org>2015-10-06 20:00:21 +0000
commitb3481e2f5f20d01c55c9f6d5f5b8b65d1d42c223 (patch)
tree6d61aba7d7d8f8e3fcff50bf1f82a9797db97635
parent8fd6c67f04a47afdd66ad4a6ca5f23fa5a1f04a7 (diff)
downloadchromium_src-b3481e2f5f20d01c55c9f6d5f5b8b65d1d42c223.zip
chromium_src-b3481e2f5f20d01c55c9f6d5f5b8b65d1d42c223.tar.gz
chromium_src-b3481e2f5f20d01c55c9f6d5f5b8b65d1d42c223.tar.bz2
ipc: Write privileged and unprivileged attachment brokers for Mac.
This is in preparation for brokering mach ports on Mac. BUG=535711 Review URL: https://codereview.chromium.org/1376473005 Cr-Commit-Position: refs/heads/master@{#352672}
-rw-r--r--ipc/BUILD.gn5
-rw-r--r--ipc/attachment_broker_messages.h31
-rw-r--r--ipc/attachment_broker_privileged_mac.cc275
-rw-r--r--ipc/attachment_broker_privileged_mac.h87
-rw-r--r--ipc/attachment_broker_privileged_mac_unittest.cc476
-rw-r--r--ipc/attachment_broker_unprivileged_mac.cc100
-rw-r--r--ipc/attachment_broker_unprivileged_mac.h41
-rw-r--r--ipc/ipc.gyp1
-rw-r--r--ipc/ipc.gypi4
9 files changed, 1020 insertions, 0 deletions
diff --git a/ipc/BUILD.gn b/ipc/BUILD.gn
index 4327bd1..ae64490 100644
--- a/ipc/BUILD.gn
+++ b/ipc/BUILD.gn
@@ -11,10 +11,14 @@ component("ipc") {
"attachment_broker_messages.h",
"attachment_broker_privileged.cc",
"attachment_broker_privileged.h",
+ "attachment_broker_privileged_mac.cc",
+ "attachment_broker_privileged_mac.h",
"attachment_broker_privileged_win.cc",
"attachment_broker_privileged_win.h",
"attachment_broker_unprivileged.cc",
"attachment_broker_unprivileged.h",
+ "attachment_broker_unprivileged_mac.cc",
+ "attachment_broker_unprivileged_mac.h",
"attachment_broker_unprivileged_win.cc",
"attachment_broker_unprivileged_win.h",
"brokerable_attachment.cc",
@@ -138,6 +142,7 @@ if (!is_android) {
test("ipc_tests") {
sources = [
+ "attachment_broker_privileged_mac_unittest.cc",
"attachment_broker_privileged_win_unittest.cc",
"attachment_broker_unprivileged_win_unittest.cc",
"ipc_channel_posix_unittest.cc",
diff --git a/ipc/attachment_broker_messages.h b/ipc/attachment_broker_messages.h
index f0e103d..a6301c2 100644
--- a/ipc/attachment_broker_messages.h
+++ b/ipc/attachment_broker_messages.h
@@ -14,6 +14,10 @@
#include "ipc/handle_attachment_win.h"
#endif // defined(OS_WIN)
+#if defined(OS_MACOSX)
+#include "ipc/mach_port_attachment_mac.h"
+#endif // defined(OS_MACOSX)
+
// ----------------------------------------------------------------------------
// Serialization of structs.
// ----------------------------------------------------------------------------
@@ -30,6 +34,14 @@ IPC_STRUCT_TRAITS_BEGIN(IPC::internal::HandleAttachmentWin::WireFormat)
IPC_STRUCT_TRAITS_END()
#endif // defined(OS_WIN)
+#if defined(OS_MACOSX)
+IPC_STRUCT_TRAITS_BEGIN(IPC::internal::MachPortAttachmentMac::WireFormat)
+ IPC_STRUCT_TRAITS_MEMBER(mach_port)
+ IPC_STRUCT_TRAITS_MEMBER(destination_process)
+ IPC_STRUCT_TRAITS_MEMBER(attachment_id)
+IPC_STRUCT_TRAITS_END()
+#endif // defined(OS_MACOSX)
+
#undef IPC_MESSAGE_EXPORT
#define IPC_MESSAGE_EXPORT IPC_EXPORT
#define IPC_MESSAGE_START AttachmentBrokerMsgStart
@@ -47,6 +59,16 @@ IPC_MESSAGE_CONTROL1(
IPC::internal::HandleAttachmentWin::WireFormat /* wire_format */)
#endif // defined(OS_WIN)
+#if defined(OS_MACOSX)
+// Sent from a broker process to a non-broker process to indicate that an OSX
+// Mach port has been duplicated. Contains all information necessary for the
+// non-broker process to translate a BrokerAttachment::AttachmentId to a
+// BrokerAttachment.
+IPC_MESSAGE_CONTROL1(
+ AttachmentBrokerMsg_MachPortHasBeenDuplicated,
+ IPC::internal::MachPortAttachmentMac::WireFormat /* wire_format */)
+#endif // defined(OS_MACOSX)
+
// ----------------------------------------------------------------------------
// Messages sent from a non-broker process to a broker process.
// ----------------------------------------------------------------------------
@@ -59,3 +81,12 @@ IPC_MESSAGE_CONTROL1(
AttachmentBrokerMsg_DuplicateWinHandle,
IPC::internal::HandleAttachmentWin::WireFormat /* wire_format */)
#endif // defined(OS_WIN)
+
+#if defined(OS_MACOSX)
+// Sent from a non-broker process to a broker process to request the duplication
+// of a Mach port into a different process (possibly the broker process, or even
+// the original process).
+IPC_MESSAGE_CONTROL1(
+ AttachmentBrokerMsg_DuplicateMachPort,
+ IPC::internal::MachPortAttachmentMac::WireFormat /* wire_format */)
+#endif // defined(OS_MACOSX)
diff --git a/ipc/attachment_broker_privileged_mac.cc b/ipc/attachment_broker_privileged_mac.cc
new file mode 100644
index 0000000..7399326
--- /dev/null
+++ b/ipc/attachment_broker_privileged_mac.cc
@@ -0,0 +1,275 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/attachment_broker_privileged_mac.h"
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/shared_memory.h"
+#include "base/process/process.h"
+#include "ipc/attachment_broker_messages.h"
+#include "ipc/brokerable_attachment.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/mach_port_attachment_mac.h"
+
+namespace {
+
+// Struct for sending a complex Mach message.
+struct MachSendComplexMessage {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t data;
+};
+
+// Sends a Mach port to |endpoint|. Assumes that |endpoint| is a send once
+// right. Takes ownership of |endpoint|.
+kern_return_t SendMachPort(mach_port_t endpoint,
+ mach_port_t port_to_send,
+ int disposition) {
+ MachSendComplexMessage send_msg;
+ send_msg.header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE, 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 = port_to_send;
+ send_msg.data.disposition = disposition;
+ send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR;
+
+ return mach_msg(&send_msg.header,
+ MACH_SEND_MSG | MACH_SEND_TIMEOUT,
+ send_msg.header.msgh_size,
+ 0, // receive limit
+ MACH_PORT_NULL, // receive name
+ 0, // timeout
+ MACH_PORT_NULL); // notification port
+}
+
+} // namespace
+
+namespace IPC {
+
+AttachmentBrokerPrivilegedMac::AttachmentBrokerPrivilegedMac()
+ : port_provider_(nullptr) {}
+
+AttachmentBrokerPrivilegedMac::~AttachmentBrokerPrivilegedMac() {}
+
+void AttachmentBrokerPrivilegedMac::SetPortProvider(
+ base::PortProvider* port_provider) {
+ CHECK(!port_provider_);
+ port_provider_ = port_provider;
+}
+
+bool AttachmentBrokerPrivilegedMac::SendAttachmentToProcess(
+ const BrokerableAttachment* attachment,
+ base::ProcessId destination_process) {
+ switch (attachment->GetBrokerableType()) {
+ case BrokerableAttachment::MACH_PORT: {
+ const internal::MachPortAttachmentMac* mach_port_attachment =
+ static_cast<const internal::MachPortAttachmentMac*>(attachment);
+ MachPortWireFormat wire_format =
+ mach_port_attachment->GetWireFormat(destination_process);
+ MachPortWireFormat new_wire_format =
+ DuplicateMachPort(wire_format, base::Process::Current().Pid());
+ if (new_wire_format.mach_port == 0)
+ return false;
+ RouteDuplicatedMachPort(new_wire_format);
+ return true;
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+ return false;
+}
+
+bool AttachmentBrokerPrivilegedMac::OnMessageReceived(const Message& msg) {
+ bool handled = true;
+ switch (msg.type()) {
+ IPC_MESSAGE_HANDLER_GENERIC(AttachmentBrokerMsg_DuplicateMachPort,
+ OnDuplicateMachPort(msg))
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ }
+ return handled;
+}
+
+void AttachmentBrokerPrivilegedMac::OnDuplicateMachPort(
+ const IPC::Message& message) {
+ AttachmentBrokerMsg_DuplicateMachPort::Param param;
+ if (!AttachmentBrokerMsg_DuplicateMachPort::Read(&message, &param))
+ return;
+ IPC::internal::MachPortAttachmentMac::WireFormat wire_format =
+ base::get<0>(param);
+
+ if (wire_format.destination_process == base::kNullProcessId) {
+ LogError(NO_DESTINATION);
+ return;
+ }
+
+ MachPortWireFormat new_wire_format =
+ DuplicateMachPort(wire_format, message.get_sender_pid());
+ RouteDuplicatedMachPort(new_wire_format);
+}
+
+void AttachmentBrokerPrivilegedMac::RouteDuplicatedMachPort(
+ const MachPortWireFormat& wire_format) {
+ // This process is the destination.
+ if (wire_format.destination_process == base::Process::Current().Pid()) {
+ scoped_refptr<BrokerableAttachment> attachment(
+ new internal::MachPortAttachmentMac(wire_format));
+ HandleReceivedAttachment(attachment);
+ return;
+ }
+
+ // Another process is the destination.
+ base::ProcessId dest = wire_format.destination_process;
+ Sender* sender = GetSenderWithProcessId(dest);
+ if (!sender) {
+ // Assuming that this message was not sent from a malicious process, the
+ // channel endpoint that would have received this message will block
+ // forever.
+ LOG(ERROR) << "Failed to deliver brokerable attachment to process with id: "
+ << dest;
+ LogError(DESTINATION_NOT_FOUND);
+ return;
+ }
+
+ LogError(DESTINATION_FOUND);
+ sender->Send(new AttachmentBrokerMsg_MachPortHasBeenDuplicated(wire_format));
+}
+
+AttachmentBrokerPrivilegedMac::MachPortWireFormat
+AttachmentBrokerPrivilegedMac::DuplicateMachPort(
+ const MachPortWireFormat& wire_format,
+ base::ProcessId source_pid) {
+ // If the source is the destination, just increment the ref count.
+ if (source_pid == wire_format.destination_process) {
+ mach_port_t task_port =
+ port_provider_->TaskForPid(wire_format.destination_process);
+ kern_return_t kr = mach_port_mod_refs(task_port, wire_format.mach_port,
+ MACH_PORT_RIGHT_SEND, 1);
+ if (kr != KERN_SUCCESS) {
+ // TODO(erikchen): UMA metric.
+ return CopyWireFormat(wire_format, MACH_PORT_NULL);
+ }
+ return wire_format;
+ }
+
+ // Acquire a send right to the memory object.
+ base::mac::ScopedMachSendRight memory_object(
+ AcquireSendRight(source_pid, wire_format.mach_port));
+ if (!memory_object)
+ return CopyWireFormat(wire_format, MACH_PORT_NULL);
+
+ mach_port_t task_port =
+ port_provider_->TaskForPid(wire_format.destination_process);
+ mach_port_name_t inserted_memory_object =
+ InsertIndirectMachPort(task_port, memory_object);
+ return CopyWireFormat(wire_format, inserted_memory_object);
+}
+
+mach_port_name_t AttachmentBrokerPrivilegedMac::InsertIndirectMachPort(
+ mach_port_t task_port,
+ mach_port_t port_to_insert) {
+ DCHECK_NE(mach_task_self(), task_port);
+
+ // Make a port with receive rights in the destination task.
+ mach_port_name_t endpoint;
+ kern_return_t kr =
+ mach_port_allocate(task_port, MACH_PORT_RIGHT_RECEIVE, &endpoint);
+ if (kr != KERN_SUCCESS) {
+ // TODO(erikchen): UMA metric.
+ return MACH_PORT_NULL;
+ }
+
+ // 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_port, endpoint, MACH_PORT_LIMITS_INFO,
+ reinterpret_cast<mach_port_info_t>(&limits),
+ MACH_PORT_LIMITS_INFO_COUNT);
+ if (kr != KERN_SUCCESS) {
+ // TODO(erikchen): UMA metric.
+ mach_port_deallocate(task_port, endpoint);
+ return MACH_PORT_NULL;
+ }
+
+ // Get a send right.
+ mach_port_t send_once_right;
+ mach_msg_type_name_t send_right_type;
+ kr =
+ mach_port_extract_right(task_port, endpoint, MACH_MSG_TYPE_MAKE_SEND_ONCE,
+ &send_once_right, &send_right_type);
+ if (kr != KERN_SUCCESS) {
+ // TODO(erikchen): UMA metric.
+ mach_port_deallocate(task_port, endpoint);
+ return MACH_PORT_NULL;
+ }
+ DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE),
+ send_right_type);
+
+ // This call takes ownership of |send_once_right|.
+ kr = SendMachPort(send_once_right, port_to_insert, MACH_MSG_TYPE_COPY_SEND);
+ if (kr != KERN_SUCCESS) {
+ // TODO(erikchen): UMA metric.
+ mach_port_deallocate(task_port, endpoint);
+ return MACH_PORT_NULL;
+ }
+
+ // Endpoint is intentionally leaked into the destination task. An IPC must be
+ // sent to the destination task so that it can clean up this port.
+ return endpoint;
+}
+
+base::mac::ScopedMachSendRight AttachmentBrokerPrivilegedMac::AcquireSendRight(
+ base::ProcessId pid,
+ mach_port_name_t named_right) {
+ if (pid == base::GetCurrentProcId()) {
+ kern_return_t kr = mach_port_mod_refs(mach_task_self(), named_right,
+ MACH_PORT_RIGHT_SEND, 1);
+ if (kr != KERN_SUCCESS)
+ return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
+ return base::mac::ScopedMachSendRight(named_right);
+ }
+
+ mach_port_t task_port = port_provider_->TaskForPid(pid);
+ return ExtractNamedRight(task_port, named_right);
+}
+
+base::mac::ScopedMachSendRight AttachmentBrokerPrivilegedMac::ExtractNamedRight(
+ mach_port_t task_port,
+ mach_port_name_t named_right) {
+ mach_port_t extracted_right = MACH_PORT_NULL;
+ mach_msg_type_name_t extracted_right_type;
+ kern_return_t kr =
+ mach_port_extract_right(task_port, named_right, MACH_MSG_TYPE_COPY_SEND,
+ &extracted_right, &extracted_right_type);
+ if (kr != KERN_SUCCESS)
+ return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
+
+ DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
+ extracted_right_type);
+
+ // Decrement the reference count of the send right from the source process.
+ kr = mach_port_mod_refs(task_port, named_right, MACH_PORT_RIGHT_SEND, -1);
+ if (kr != KERN_SUCCESS) {
+ // TODO(erikchen): UMA metric.
+ // Failure does not actually affect attachment brokering, so there's no need
+ // to return |MACH_PORT_NULL|.
+ }
+
+ return base::mac::ScopedMachSendRight(extracted_right);
+}
+
+AttachmentBrokerPrivilegedMac::MachPortWireFormat
+AttachmentBrokerPrivilegedMac::CopyWireFormat(
+ const MachPortWireFormat& wire_format,
+ uint32_t mach_port) {
+ return MachPortWireFormat(mach_port, wire_format.destination_process,
+ wire_format.attachment_id);
+}
+
+} // namespace IPC
diff --git a/ipc/attachment_broker_privileged_mac.h b/ipc/attachment_broker_privileged_mac.h
new file mode 100644
index 0000000..24c8b0c
--- /dev/null
+++ b/ipc/attachment_broker_privileged_mac.h
@@ -0,0 +1,87 @@
+// Copyright 2015 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 IPC_ATTACHMENT_BROKER_PRIVILEGED_MAC_H_
+#define IPC_ATTACHMENT_BROKER_PRIVILEGED_MAC_H_
+
+#include <mach/mach.h>
+
+#include "base/gtest_prod_util.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/process/port_provider_mac.h"
+#include "ipc/attachment_broker_privileged.h"
+#include "ipc/ipc_export.h"
+#include "ipc/mach_port_attachment_mac.h"
+
+namespace IPC {
+
+// This class is a concrete subclass of AttachmentBrokerPrivileged for the
+// OSX platform.
+class IPC_EXPORT AttachmentBrokerPrivilegedMac
+ : public AttachmentBrokerPrivileged {
+ public:
+ AttachmentBrokerPrivilegedMac();
+ ~AttachmentBrokerPrivilegedMac() override;
+
+ // The port provider must live as long as the AttachmentBrokerPrivilegedMac. A
+ // port provider must be set before any attachment brokering occurs.
+ void SetPortProvider(base::PortProvider* port_provider);
+
+ // IPC::AttachmentBroker overrides.
+ bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+ base::ProcessId destination_process) override;
+
+ // IPC::Listener overrides.
+ bool OnMessageReceived(const Message& message) override;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
+ InsertRight);
+ FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
+ InsertSameRightTwice);
+ FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
+ InsertTwoRights);
+ using MachPortWireFormat = internal::MachPortAttachmentMac::WireFormat;
+ // IPC message handlers.
+ void OnDuplicateMachPort(const Message& message);
+
+ // Duplicates the Mach port referenced from |wire_format| from
+ // |source_process| into |wire_format|'s destination process.
+ MachPortWireFormat DuplicateMachPort(const MachPortWireFormat& wire_format,
+ base::ProcessId source_process);
+
+ // Returns the name of the inserted right of a port, which contains a queued
+ // message with |port_to_insert|. Returns |MACH_PORT_NULL| on failure.
+ // |task_port| must be for a different task, and |port_to_insert| is a port
+ // right in the current task.
+ mach_port_name_t InsertIndirectMachPort(mach_port_t task_port,
+ mach_port_t port_to_insert);
+
+ // Acquire a send right to a named right in |pid|.
+ // Returns MACH_PORT_NULL on error.
+ base::mac::ScopedMachSendRight AcquireSendRight(base::ProcessId pid,
+ mach_port_name_t named_right);
+
+ // Extracts a copy of the send right to |named_right| from |task_port|.
+ // Returns MACH_PORT_NULL on error.
+ base::mac::ScopedMachSendRight ExtractNamedRight(
+ mach_port_t task_port,
+ mach_port_name_t named_right);
+
+ // Copies an existing |wire_format|, but substitutes in a different mach port.
+ MachPortWireFormat CopyWireFormat(const MachPortWireFormat& wire_format,
+ uint32_t mach_port);
+
+ // If the mach port's destination is this process, queue it and notify the
+ // observers. Otherwise, send it in an IPC to its destination.
+ void RouteDuplicatedMachPort(const MachPortWireFormat& wire_format);
+
+ base::PortProvider* port_provider_;
+
+ DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerPrivilegedMac);
+};
+
+} // namespace IPC
+
+#endif // IPC_ATTACHMENT_BROKER_PRIVILEGED_MAC_H_
diff --git a/ipc/attachment_broker_privileged_mac_unittest.cc b/ipc/attachment_broker_privileged_mac_unittest.cc
new file mode 100644
index 0000000..b6992aa
--- /dev/null
+++ b/ipc/attachment_broker_privileged_mac_unittest.cc
@@ -0,0 +1,476 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/attachment_broker_privileged_mac.h"
+
+#include <mach/mach.h>
+#include <mach/mach_vm.h>
+#include <servers/bootstrap.h>
+
+#include <map>
+
+#include "base/command_line.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/memory/shared_memory.h"
+#include "base/process/port_provider_mac.h"
+#include "base/process/process_handle.h"
+#include "base/rand_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/test/multiprocess_test.h"
+#include "base/test/test_timeouts.h"
+#include "testing/multiprocess_func_list.h"
+
+namespace IPC {
+
+namespace {
+
+static const std::string g_service_switch_name = "service_name";
+
+// Structs used to pass a mach port from client to server.
+struct MachSendPortMessage {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t data;
+};
+struct MachReceivePortMessage : public MachSendPortMessage {
+ mach_msg_trailer_t trailer;
+};
+
+// Makes the current process into a Mach Server with the given |service_name|.
+base::mac::ScopedMachSendRight BecomeMachServer(const char* service_name) {
+ mach_port_t port;
+ kern_return_t kr = bootstrap_check_in(bootstrap_port, service_name, &port);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "BecomeMachServer";
+ return base::mac::ScopedMachSendRight(port);
+}
+
+// Returns the mach port for the Mach Server with the given |service_name|.
+base::mac::ScopedMachSendRight LookupServer(const char* service_name) {
+ mach_port_t server_port;
+ kern_return_t kr =
+ bootstrap_look_up(bootstrap_port, service_name, &server_port);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "LookupServer";
+ return base::mac::ScopedMachSendRight(server_port);
+}
+
+base::mac::ScopedMachReceiveRight MakeReceivingPort() {
+ mach_port_t client_port;
+ kern_return_t kr =
+ mach_port_allocate(mach_task_self(), // our task is acquiring
+ MACH_PORT_RIGHT_RECEIVE, // a new receive right
+ &client_port); // with this name
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "MakeReceivingPort";
+ return base::mac::ScopedMachReceiveRight(client_port);
+}
+
+// Blocks until a mach message is sent to |server_port|. This mach message
+// must contain a mach port. Returns that mach port.
+base::mac::ScopedMachSendRight ReceiveMachPort(mach_port_t port_to_listen_on) {
+ MachReceivePortMessage recv_msg;
+ mach_msg_header_t* recv_hdr = &recv_msg.header;
+ recv_hdr->msgh_local_port = port_to_listen_on;
+ recv_hdr->msgh_size = sizeof(recv_msg);
+ kern_return_t kr =
+ mach_msg(recv_hdr, MACH_RCV_MSG, 0, recv_hdr->msgh_size,
+ port_to_listen_on, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "ReceiveMachPort";
+ mach_port_t other_task_port = recv_msg.data.name;
+ return base::mac::ScopedMachSendRight(other_task_port);
+}
+
+// Passes a copy of the send right of |port_to_send| to |receiving_port|.
+void SendMachPort(mach_port_t receiving_port,
+ mach_port_t port_to_send,
+ int disposition) {
+ MachSendPortMessage send_msg;
+ send_msg.header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
+ send_msg.header.msgh_size = sizeof(send_msg);
+ send_msg.header.msgh_remote_port = receiving_port;
+ 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 = port_to_send;
+ send_msg.data.disposition = disposition;
+ send_msg.data.type = MACH_MSG_PORT_DESCRIPTOR;
+ int kr = mach_msg(&send_msg.header, MACH_SEND_MSG, send_msg.header.msgh_size,
+ 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "SendMachPort";
+}
+
+// Sends a uint32_t to a mach port.
+void SendUInt32(mach_port_t port, uint32_t message) {
+ int message_size = sizeof(uint32_t);
+ int total_size = message_size + sizeof(mach_msg_header_t);
+ void* buffer = malloc(total_size);
+ mach_msg_header_t* header = (mach_msg_header_t*)buffer;
+ header->msgh_remote_port = port;
+ header->msgh_local_port = MACH_PORT_NULL;
+ header->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
+ header->msgh_reserved = 0;
+ header->msgh_size = total_size;
+ memcpy(static_cast<char*>(buffer) + sizeof(mach_msg_header_t), &message,
+ message_size);
+
+ kern_return_t kr;
+ kr = mach_msg(static_cast<mach_msg_header_t*>(buffer), MACH_SEND_MSG,
+ total_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
+ MACH_PORT_NULL);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "SendUInt32";
+ free(buffer);
+}
+
+// Receives a uint32_t from a mach port.
+uint32_t ReceiveUInt32(mach_port_t listening_port) {
+ int message_size = sizeof(uint32_t);
+ int total_size =
+ message_size + sizeof(mach_msg_header_t) + sizeof(mach_msg_trailer_t);
+ int options = MACH_RCV_MSG;
+ void* buffer = malloc(total_size);
+
+ int kr =
+ mach_msg(static_cast<mach_msg_header_t*>(buffer), options, 0, total_size,
+ listening_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "ReceiveUInt32";
+
+ uint32_t response;
+ memcpy(&response, static_cast<char*>(buffer) + sizeof(mach_msg_header_t),
+ message_size);
+
+ free(buffer);
+ return response;
+}
+
+std::string CreateRandomServiceName() {
+ return base::StringPrintf(
+ "AttachmentBrokerPrivilegedMacMultiProcessTest.%llu", base::RandUint64());
+}
+
+// The number of active names in the current task's port name space.
+mach_msg_type_number_t GetActiveNameCount() {
+ mach_port_name_array_t name_array;
+ mach_msg_type_number_t names_count;
+ mach_port_type_array_t type_array;
+ mach_msg_type_number_t types_count;
+ kern_return_t kr = mach_port_names(mach_task_self(), &name_array,
+ &names_count, &type_array, &types_count);
+ MACH_CHECK(kr == KERN_SUCCESS, kr) << "GetActiveNameCount";
+ return names_count;
+}
+
+// Sets up the mach communication ports with the server. Returns a port to which
+// the server will send mach objects.
+// |original_name_count| is an output variable that describes the number of
+// active names in this task before the task port is shared with the server.
+base::mac::ScopedMachReceiveRight CommonChildProcessSetUp(
+ mach_msg_type_number_t* original_name_count) {
+ base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess();
+ std::string service_name =
+ cmd_line.GetSwitchValueASCII(g_service_switch_name);
+ base::mac::ScopedMachSendRight server_port(
+ LookupServer(service_name.c_str()));
+ base::mac::ScopedMachReceiveRight client_port(MakeReceivingPort());
+
+ // |server_port| is a newly allocated right which will be deallocated once
+ // this method returns.
+ *original_name_count = GetActiveNameCount() - 1;
+
+ // Send the port that this process is listening on to the server.
+ SendMachPort(server_port, client_port, MACH_MSG_TYPE_MAKE_SEND);
+
+ // Send the task port for this process.
+ SendMachPort(server_port, mach_task_self(), MACH_MSG_TYPE_COPY_SEND);
+ return client_port;
+}
+
+// Creates a new shared memory region populated with 'a'.
+scoped_ptr<base::SharedMemory> CreateAndPopulateSharedMemoryHandle(
+ size_t size) {
+ base::SharedMemoryHandle shm(size);
+ scoped_ptr<base::SharedMemory> shared_memory(
+ new base::SharedMemory(shm, false));
+ shared_memory->Map(size);
+ memset(shared_memory->memory(), 'a', size);
+ return shared_memory;
+}
+
+// Create a shared memory region from a memory object. The returned object takes
+// ownership of |memory_object|.
+scoped_ptr<base::SharedMemory> MapMemoryObject(mach_port_t memory_object,
+ size_t size) {
+ base::SharedMemoryHandle shm(memory_object, size, base::GetCurrentProcId());
+ scoped_ptr<base::SharedMemory> shared_memory(
+ new base::SharedMemory(shm, false));
+ shared_memory->Map(size);
+ return shared_memory;
+}
+
+} // namespace
+
+class AttachmentBrokerPrivilegedMacMultiProcessTest
+ : public base::MultiProcessTest {
+ public:
+ AttachmentBrokerPrivilegedMacMultiProcessTest() {}
+
+ base::CommandLine MakeCmdLine(const std::string& procname) override {
+ base::CommandLine command_line = MultiProcessTest::MakeCmdLine(procname);
+ // Pass the service name to the child process.
+ command_line.AppendSwitchASCII(g_service_switch_name, service_name_);
+ return command_line;
+ }
+
+ void SetUpChild(const std::string& name) {
+ // Make a random service name so that this test doesn't conflict with other
+ // similar tests.
+ service_name_ = CreateRandomServiceName();
+ server_port_.reset(BecomeMachServer(service_name_.c_str()).release());
+ child_process_ = SpawnChild(name);
+ client_port_.reset(ReceiveMachPort(server_port_).release());
+ client_task_port_.reset(ReceiveMachPort(server_port_).release());
+ }
+
+ static const int s_memory_size = 99999;
+
+ protected:
+ std::string service_name_;
+
+ // A port on which the main process listens for mach messages from the child
+ // process.
+ base::mac::ScopedMachReceiveRight server_port_;
+
+ // A port on which the child process listens for mach messages from the main
+ // process.
+ base::mac::ScopedMachSendRight client_port_;
+
+ // Child process's task port.
+ base::mac::ScopedMachSendRight client_task_port_;
+
+ base::Process child_process_;
+ DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerPrivilegedMacMultiProcessTest);
+};
+
+// The attachment broker inserts a right for a memory object into the
+// destination task.
+TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertRight) {
+ SetUpChild("InsertRightClient");
+ mach_msg_type_number_t original_name_count = GetActiveNameCount();
+ IPC::AttachmentBrokerPrivilegedMac broker;
+
+ // Create some shared memory.
+ scoped_ptr<base::SharedMemory> shared_memory =
+ CreateAndPopulateSharedMemoryHandle(s_memory_size);
+ ASSERT_TRUE(shared_memory->handle().IsValid());
+
+ // Insert it indirectly into the destination task.
+ mach_port_name_t inserted_memory_object = broker.InsertIndirectMachPort(
+ client_task_port_, shared_memory->handle().GetMemoryObject());
+ EXPECT_NE(inserted_memory_object,
+ static_cast<mach_port_name_t>(MACH_PORT_NULL));
+ SendUInt32(client_port_, inserted_memory_object);
+
+ // Check that no names have been leaked.
+ shared_memory.reset();
+ EXPECT_EQ(original_name_count, GetActiveNameCount());
+
+ int rv = -1;
+ ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
+ TestTimeouts::action_timeout(), &rv));
+ EXPECT_EQ(0, rv);
+}
+
+MULTIPROCESS_TEST_MAIN(InsertRightClient) {
+ mach_msg_type_number_t original_name_count = 0;
+ base::mac::ScopedMachReceiveRight client_port(
+ CommonChildProcessSetUp(&original_name_count).release());
+ base::mac::ScopedMachReceiveRight inserted_port(ReceiveUInt32(client_port));
+ base::mac::ScopedMachSendRight memory_object(ReceiveMachPort(inserted_port));
+ inserted_port.reset();
+
+ // The server should have inserted a right into this process.
+ EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
+
+ // Map the memory object and check its contents.
+ scoped_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
+ memory_object.release(),
+ AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
+ const char* start = static_cast<const char*>(shared_memory->memory());
+ for (int i = 0;
+ i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
+ DCHECK_EQ(start[i], 'a');
+ }
+
+ // Check that no names have been leaked.
+ shared_memory.reset();
+ EXPECT_EQ(original_name_count, GetActiveNameCount());
+
+ return 0;
+}
+
+// The attachment broker inserts the right for a memory object into the
+// destination task twice.
+TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertSameRightTwice) {
+ SetUpChild("InsertSameRightTwiceClient");
+ mach_msg_type_number_t original_name_count = GetActiveNameCount();
+ IPC::AttachmentBrokerPrivilegedMac broker;
+
+ // Create some shared memory.
+ scoped_ptr<base::SharedMemory> shared_memory =
+ CreateAndPopulateSharedMemoryHandle(s_memory_size);
+ ASSERT_TRUE(shared_memory->handle().IsValid());
+
+ // Insert it indirectly into the destination task, twice.
+ for (int i = 0; i < 2; ++i) {
+ mach_port_name_t inserted_memory_object = broker.InsertIndirectMachPort(
+ client_task_port_, shared_memory->handle().GetMemoryObject());
+ EXPECT_NE(inserted_memory_object,
+ static_cast<mach_port_name_t>(MACH_PORT_NULL));
+ SendUInt32(client_port_, inserted_memory_object);
+ }
+
+ // Check that no names have been leaked.
+ shared_memory.reset();
+ EXPECT_EQ(original_name_count, GetActiveNameCount());
+
+ int rv = -1;
+ ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
+ TestTimeouts::action_timeout(), &rv));
+ EXPECT_EQ(0, rv);
+}
+
+MULTIPROCESS_TEST_MAIN(InsertSameRightTwiceClient) {
+ mach_msg_type_number_t original_name_count = 0;
+ base::mac::ScopedMachReceiveRight client_port(
+ CommonChildProcessSetUp(&original_name_count).release());
+
+ // Receive two memory objects.
+ base::mac::ScopedMachReceiveRight inserted_port(ReceiveUInt32(client_port));
+ base::mac::ScopedMachReceiveRight inserted_port2(ReceiveUInt32(client_port));
+ base::mac::ScopedMachSendRight memory_object(ReceiveMachPort(inserted_port));
+ base::mac::ScopedMachSendRight memory_object2(
+ ReceiveMachPort(inserted_port2));
+ inserted_port.reset();
+ inserted_port2.reset();
+
+ // Both rights are for the same Mach port, so only one new name should appear.
+ EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
+
+ // Map both memory objects and check their contents.
+ scoped_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
+ memory_object.release(),
+ AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
+ char* start = static_cast<char*>(shared_memory->memory());
+ for (int i = 0;
+ i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
+ DCHECK_EQ(start[i], 'a');
+ }
+
+ scoped_ptr<base::SharedMemory> shared_memory2(MapMemoryObject(
+ memory_object2.release(),
+ AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
+ char* start2 = static_cast<char*>(shared_memory2->memory());
+ for (int i = 0;
+ i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
+ DCHECK_EQ(start2[i], 'a');
+ }
+
+ // Check that the contents of both regions are shared.
+ start[0] = 'b';
+ DCHECK_EQ(start2[0], 'b');
+
+ // After releasing one shared memory region, the name count shouldn't change,
+ // since another reference exists.
+ shared_memory.reset();
+ EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
+
+ // After releasing the second shared memory region, the name count should be
+ // as if no names were ever inserted
+ shared_memory2.reset();
+ EXPECT_EQ(original_name_count, GetActiveNameCount());
+
+ return 0;
+}
+
+// The attachment broker inserts the rights for two memory objects into the
+// destination task.
+TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertTwoRights) {
+ SetUpChild("InsertTwoRightsClient");
+ mach_msg_type_number_t original_name_count = GetActiveNameCount();
+ IPC::AttachmentBrokerPrivilegedMac broker;
+
+ for (int i = 0; i < 2; ++i) {
+ // Create some shared memory.
+ scoped_ptr<base::SharedMemory> shared_memory =
+ CreateAndPopulateSharedMemoryHandle(s_memory_size);
+ ASSERT_TRUE(shared_memory->handle().IsValid());
+
+ // Insert it indirectly into the destination task.
+ mach_port_name_t inserted_memory_object = broker.InsertIndirectMachPort(
+ client_task_port_, shared_memory->handle().GetMemoryObject());
+ EXPECT_NE(inserted_memory_object,
+ static_cast<mach_port_name_t>(MACH_PORT_NULL));
+ SendUInt32(client_port_, inserted_memory_object);
+ }
+
+ // Check that no names have been leaked.
+ EXPECT_EQ(original_name_count, GetActiveNameCount());
+
+ int rv = -1;
+ ASSERT_TRUE(child_process_.WaitForExitWithTimeout(
+ TestTimeouts::action_timeout(), &rv));
+ EXPECT_EQ(0, rv);
+}
+
+MULTIPROCESS_TEST_MAIN(InsertTwoRightsClient) {
+ mach_msg_type_number_t original_name_count = 0;
+ base::mac::ScopedMachReceiveRight client_port(
+ CommonChildProcessSetUp(&original_name_count).release());
+
+ // Receive two memory objects.
+ base::mac::ScopedMachReceiveRight inserted_port(ReceiveUInt32(client_port));
+ base::mac::ScopedMachReceiveRight inserted_port2(ReceiveUInt32(client_port));
+ base::mac::ScopedMachSendRight memory_object(ReceiveMachPort(inserted_port));
+ base::mac::ScopedMachSendRight memory_object2(
+ ReceiveMachPort(inserted_port2));
+ inserted_port.reset();
+ inserted_port2.reset();
+
+ // There should be two new names to reflect the two new shared memory regions.
+ EXPECT_EQ(original_name_count + 2, GetActiveNameCount());
+
+ // Map both memory objects and check their contents.
+ scoped_ptr<base::SharedMemory> shared_memory(MapMemoryObject(
+ memory_object.release(),
+ AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
+ char* start = static_cast<char*>(shared_memory->memory());
+ for (int i = 0;
+ i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
+ DCHECK_EQ(start[i], 'a');
+ }
+
+ scoped_ptr<base::SharedMemory> shared_memory2(MapMemoryObject(
+ memory_object2.release(),
+ AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size));
+ char* start2 = static_cast<char*>(shared_memory2->memory());
+ for (int i = 0;
+ i < AttachmentBrokerPrivilegedMacMultiProcessTest::s_memory_size; ++i) {
+ DCHECK_EQ(start2[i], 'a');
+ }
+
+ // Check that the contents of both regions are not shared.
+ start[0] = 'b';
+ DCHECK_EQ(start2[0], 'a');
+
+ // After releasing one shared memory region, the name count should decrement.
+ shared_memory.reset();
+ EXPECT_EQ(original_name_count + 1, GetActiveNameCount());
+ shared_memory2.reset();
+ EXPECT_EQ(original_name_count, GetActiveNameCount());
+
+ return 0;
+}
+
+} // namespace IPC
diff --git a/ipc/attachment_broker_unprivileged_mac.cc b/ipc/attachment_broker_unprivileged_mac.cc
new file mode 100644
index 0000000..b4ced08
--- /dev/null
+++ b/ipc/attachment_broker_unprivileged_mac.cc
@@ -0,0 +1,100 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/attachment_broker_unprivileged_mac.h"
+
+#include <mach/mach.h>
+
+#include "base/mac/scoped_mach_port.h"
+#include "base/process/process.h"
+#include "ipc/attachment_broker_messages.h"
+#include "ipc/brokerable_attachment.h"
+#include "ipc/ipc_sender.h"
+#include "ipc/mach_port_attachment_mac.h"
+
+namespace {
+
+// Struct for receiving a complex message.
+struct MachReceiveComplexMessage {
+ mach_msg_header_t header;
+ mach_msg_body_t body;
+ mach_msg_port_descriptor_t data;
+ mach_msg_trailer_t trailer;
+};
+
+// Receives a Mach port from |port_to_listen_on|, which should have exactly one
+// queued message. Returns |MACH_PORT_NULL| on any error.
+base::mac::ScopedMachSendRight ReceiveMachPort(mach_port_t port_to_listen_on) {
+ MachReceiveComplexMessage recv_msg;
+ mach_msg_header_t* recv_hdr = &recv_msg.header;
+ recv_hdr->msgh_local_port = port_to_listen_on;
+ recv_hdr->msgh_size = sizeof(recv_msg);
+
+ kern_return_t kr =
+ mach_msg(recv_hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0,
+ recv_hdr->msgh_size, port_to_listen_on, 0, MACH_PORT_NULL);
+ if (kr != KERN_SUCCESS)
+ return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
+ if (recv_msg.header.msgh_id != 0)
+ return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
+ return base::mac::ScopedMachSendRight(recv_msg.data.name);
+}
+
+} // namespace
+
+namespace IPC {
+
+AttachmentBrokerUnprivilegedMac::AttachmentBrokerUnprivilegedMac() {}
+
+AttachmentBrokerUnprivilegedMac::~AttachmentBrokerUnprivilegedMac() {}
+
+bool AttachmentBrokerUnprivilegedMac::SendAttachmentToProcess(
+ const BrokerableAttachment* attachment,
+ base::ProcessId destination_process) {
+ switch (attachment->GetBrokerableType()) {
+ case BrokerableAttachment::MACH_PORT: {
+ const internal::MachPortAttachmentMac* mach_port_attachment =
+ static_cast<const internal::MachPortAttachmentMac*>(attachment);
+ internal::MachPortAttachmentMac::WireFormat format =
+ mach_port_attachment->GetWireFormat(destination_process);
+ return get_sender()->Send(
+ new AttachmentBrokerMsg_DuplicateMachPort(format));
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+ return false;
+}
+
+bool AttachmentBrokerUnprivilegedMac::OnMessageReceived(const Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(AttachmentBrokerUnprivilegedMac, msg)
+ IPC_MESSAGE_HANDLER(AttachmentBrokerMsg_MachPortHasBeenDuplicated,
+ OnMachPortHasBeenDuplicated)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void AttachmentBrokerUnprivilegedMac::OnMachPortHasBeenDuplicated(
+ const IPC::internal::MachPortAttachmentMac::WireFormat& wire_format) {
+ // The IPC message was intended for a different process. Ignore it.
+ if (wire_format.destination_process != base::Process::Current().Pid()) {
+ // TODO(erikchen): UMA metric.
+ return;
+ }
+
+ base::mac::ScopedMachReceiveRight message_port(wire_format.mach_port);
+ base::mac::ScopedMachSendRight memory_object(ReceiveMachPort(message_port));
+ IPC::internal::MachPortAttachmentMac::WireFormat translated_wire_format(
+ memory_object.release(), wire_format.destination_process,
+ wire_format.attachment_id);
+
+ scoped_refptr<BrokerableAttachment> attachment(
+ new IPC::internal::MachPortAttachmentMac(translated_wire_format));
+ HandleReceivedAttachment(attachment);
+}
+
+} // namespace IPC
diff --git a/ipc/attachment_broker_unprivileged_mac.h b/ipc/attachment_broker_unprivileged_mac.h
new file mode 100644
index 0000000..58c4f87
--- /dev/null
+++ b/ipc/attachment_broker_unprivileged_mac.h
@@ -0,0 +1,41 @@
+// Copyright 2015 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 IPC_ATTACHMENT_BROKER_UNPRIVILEGED_MAC_H_
+#define IPC_ATTACHMENT_BROKER_UNPRIVILEGED_MAC_H_
+
+#include "ipc/attachment_broker_unprivileged.h"
+#include "ipc/ipc_export.h"
+#include "ipc/mach_port_attachment_mac.h"
+
+namespace IPC {
+
+class BrokerableAttachment;
+
+// This class is an implementation of AttachmentBroker for the OSX platform
+// for non-privileged processes.
+class IPC_EXPORT AttachmentBrokerUnprivilegedMac
+ : public IPC::AttachmentBrokerUnprivileged {
+ public:
+ AttachmentBrokerUnprivilegedMac();
+ ~AttachmentBrokerUnprivilegedMac() override;
+
+ // IPC::AttachmentBroker overrides.
+ bool SendAttachmentToProcess(const BrokerableAttachment* attachment,
+ base::ProcessId destination_process) override;
+
+ // IPC::Listener overrides.
+ bool OnMessageReceived(const Message& message) override;
+
+ private:
+ // IPC message handlers.
+ void OnMachPortHasBeenDuplicated(
+ const IPC::internal::MachPortAttachmentMac::WireFormat& wire_format);
+
+ DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerUnprivilegedMac);
+};
+
+} // namespace IPC
+
+#endif // IPC_ATTACHMENT_BROKER_UNPRIVILEGED_MAC_H_
diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp
index 7924ec6..ad178ab 100644
--- a/ipc/ipc.gyp
+++ b/ipc/ipc.gyp
@@ -52,6 +52,7 @@
'..'
],
'sources': [
+ 'attachment_broker_privileged_mac_unittest.cc',
'attachment_broker_privileged_win_unittest.cc',
'attachment_broker_unprivileged_win_unittest.cc',
'ipc_channel_posix_unittest.cc',
diff --git a/ipc/ipc.gypi b/ipc/ipc.gypi
index 696b34a..44fcdab 100644
--- a/ipc/ipc.gypi
+++ b/ipc/ipc.gypi
@@ -16,10 +16,14 @@
'attachment_broker_messages.h',
'attachment_broker_privileged.cc',
'attachment_broker_privileged.h',
+ 'attachment_broker_privileged_mac.cc',
+ 'attachment_broker_privileged_mac.h',
'attachment_broker_privileged_win.cc',
'attachment_broker_privileged_win.h',
'attachment_broker_unprivileged.cc',
'attachment_broker_unprivileged.h',
+ 'attachment_broker_unprivileged_mac.cc',
+ 'attachment_broker_unprivileged_mac.h',
'attachment_broker_unprivileged_win.cc',
'attachment_broker_unprivileged_win.h',
'brokerable_attachment.cc',