summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gypi1
-rw-r--r--base/process/BUILD.gn1
-rw-r--r--base/process/port_provider_mac.cc27
-rw-r--r--base/process/port_provider_mac.h34
-rw-r--r--ipc/attachment_broker_mac_unittest.cc152
-rw-r--r--ipc/attachment_broker_privileged.h6
-rw-r--r--ipc/attachment_broker_privileged_mac.cc227
-rw-r--r--ipc/attachment_broker_privileged_mac.h113
-rw-r--r--ipc/attachment_broker_privileged_mac_unittest.cc18
-rw-r--r--ipc/ipc_channel_reader.cc1
-rw-r--r--ipc/ipc_test_messages.h2
-rw-r--r--tools/metrics/histograms/histograms.xml7
12 files changed, 496 insertions, 93 deletions
diff --git a/base/base.gypi b/base/base.gypi
index 04fa6c1..bdaf4c7 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -477,6 +477,7 @@
'process/memory_linux.cc',
'process/memory_mac.mm',
'process/memory_win.cc',
+ 'process/port_provider_mac.cc',
'process/port_provider_mac.h',
'process/process.h',
'process/process_handle.cc',
diff --git a/base/process/BUILD.gn b/base/process/BUILD.gn
index 8c7cc8a..63d65cd 100644
--- a/base/process/BUILD.gn
+++ b/base/process/BUILD.gn
@@ -24,6 +24,7 @@ source_set("process") {
"memory_linux.cc",
"memory_mac.mm",
"memory_win.cc",
+ "port_provider_mac.cc",
"port_provider_mac.h",
"process.h",
"process_handle.cc",
diff --git a/base/process/port_provider_mac.cc b/base/process/port_provider_mac.cc
new file mode 100644
index 0000000..ac13949
--- /dev/null
+++ b/base/process/port_provider_mac.cc
@@ -0,0 +1,27 @@
+// 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 "base/process/port_provider_mac.h"
+
+namespace base {
+
+PortProvider::PortProvider() : lock_(), observer_list_() {}
+PortProvider::~PortProvider() {}
+
+void PortProvider::AddObserver(Observer* observer) {
+ base::AutoLock l(lock_);
+ observer_list_.AddObserver(observer);
+}
+
+void PortProvider::RemoveObserver(Observer* observer) {
+ base::AutoLock l(lock_);
+ observer_list_.RemoveObserver(observer);
+}
+
+void PortProvider::NotifyObservers(ProcessHandle process) {
+ base::AutoLock l(lock_);
+ FOR_EACH_OBSERVER(Observer, observer_list_, OnReceivedTaskPort(process));
+}
+
+} // namespace base
diff --git a/base/process/port_provider_mac.h b/base/process/port_provider_mac.h
index bdee4a8..2f40297 100644
--- a/base/process/port_provider_mac.h
+++ b/base/process/port_provider_mac.h
@@ -8,7 +8,10 @@
#include <mach/mach.h>
#include "base/base_export.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
#include "base/process/process_handle.h"
+#include "base/synchronization/lock.h"
namespace base {
@@ -17,11 +20,40 @@ namespace base {
// privileges.
class BASE_EXPORT PortProvider {
public:
- virtual ~PortProvider() {}
+ PortProvider();
+ virtual ~PortProvider();
+
+ class Observer {
+ public:
+ virtual ~Observer() {};
+ // Called by the PortProvider to notify observers that the task port was
+ // received for a given process.
+ // No guarantees are made about the thread on which this notification will
+ // be sent.
+ // Observers must not call AddObserver() or RemoveObserver() in this
+ // callback, as doing so will deadlock.
+ virtual void OnReceivedTaskPort(ProcessHandle process) = 0;
+ };
// Returns the mach task port for |process| if possible, or else
// |MACH_PORT_NULL|.
virtual mach_port_t TaskForPid(ProcessHandle process) const = 0;
+
+ // Observer interface.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+
+ protected:
+ // Called by subclasses to send a notification to observers.
+ void NotifyObservers(ProcessHandle process);
+
+ private:
+ // ObserverList is not thread-safe, so |lock_| ensures consistency of
+ // |observer_list_|.
+ base::Lock lock_;
+ base::ObserverList<Observer> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(PortProvider);
};
} // namespace base
diff --git a/ipc/attachment_broker_mac_unittest.cc b/ipc/attachment_broker_mac_unittest.cc
index 3188640..2e370cd 100644
--- a/ipc/attachment_broker_mac_unittest.cc
+++ b/ipc/attachment_broker_mac_unittest.cc
@@ -14,6 +14,7 @@
#include "base/mac/mac_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/shared_memory.h"
+#include "ipc/attachment_broker_messages.h"
#include "ipc/attachment_broker_privileged_mac.h"
#include "ipc/attachment_broker_unprivileged_mac.h"
#include "ipc/ipc_listener.h"
@@ -348,8 +349,11 @@ class MockPortProvider : public base::PortProvider {
void InsertEntry(base::ProcessHandle process, mach_port_t task_port) {
port_map_[process] = task_port;
+ NotifyObservers(process);
}
+ void ClearPortMap() { port_map_.clear(); }
+
private:
std::map<base::ProcessHandle, mach_port_t> port_map_;
};
@@ -466,17 +470,23 @@ class IPCAttachmentBrokerMacTest : public IPCTestBase {
ResultListener result_listener_;
};
-using OnMessageReceivedCallback = void (*)(IPC::Sender* sender,
- const IPC::Message& message);
-
// These objects are globally accessible, and are expected to outlive all IPC
// Channels.
struct ChildProcessGlobals {
- scoped_ptr<IPC::AttachmentBrokerPrivilegedMac> broker;
MockPortProvider port_provider;
+
+ // The broker must be destroyed before the port_provider, so that the broker
+ // gets a chance to unregister itself as an observer. This doesn't matter
+ // outside of tests, since neither port_provider nor broker will ever be
+ // destroyed.
+ scoped_ptr<IPC::AttachmentBrokerPrivilegedMac> broker;
base::mac::ScopedMachSendRight server_task_port;
};
+using OnMessageReceivedCallback = void (*)(IPC::Sender* sender,
+ const IPC::Message& message,
+ ChildProcessGlobals* globals);
+
// Sets up the Mach communication ports with the server. Returns a set of
// globals that must live at least as long as the test.
scoped_ptr<ChildProcessGlobals> CommonChildProcessSetUp() {
@@ -527,7 +537,7 @@ int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback,
while (listener.has_message()) {
LOG(INFO) << "Privileged process running callback.";
- callback(channel.get(), listener.get_first_message());
+ callback(channel.get(), listener.get_first_message(), globals.get());
LOG(INFO) << "Privileged process finishing callback.";
listener.pop_first_message();
}
@@ -556,7 +566,8 @@ TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandle) {
}
void SendSharedMemoryHandleCallback(IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
bool success = CheckContentsOfMessage1(message, kDataBuffer1);
SendControlMessage(sender, success);
}
@@ -582,7 +593,8 @@ TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleLong) {
}
void SendSharedMemoryHandleLongCallback(IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
std::string buffer(1 << 23, 'a');
bool success = CheckContentsOfMessage1(message, buffer);
SendControlMessage(sender, success);
@@ -610,7 +622,8 @@ TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesDifferentSharedMemoryHandle) {
void SendTwoMessagesDifferentSharedMemoryHandleCallback(
IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
static int count = 0;
static bool success = true;
++count;
@@ -654,7 +667,8 @@ TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesSameSharedMemoryHandle) {
void SendTwoMessagesSameSharedMemoryHandleCallback(
IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
static int count = 0;
static base::SharedMemoryHandle handle1;
++count;
@@ -701,7 +715,8 @@ TEST_F(IPCAttachmentBrokerMacTest,
void SendOneMessageWithTwoDifferentSharedMemoryHandlesCallback(
IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
base::SharedMemoryHandle handle1;
base::SharedMemoryHandle handle2;
if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) {
@@ -745,7 +760,8 @@ TEST_F(IPCAttachmentBrokerMacTest,
void SendOneMessageWithTwoSameSharedMemoryHandlesCallback(
IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
base::SharedMemoryHandle handle1;
base::SharedMemoryHandle handle2;
if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) {
@@ -802,7 +818,8 @@ TEST_F(IPCAttachmentBrokerMacTest, SendPosixFDAndMachPort) {
}
void SendPosixFDAndMachPortCallback(IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
TestSharedMemoryHandleMsg3::Schema::Param p;
if (!TestSharedMemoryHandleMsg3::Read(&message, &p)) {
LOG(ERROR) << "Failed to deserialize message.";
@@ -883,7 +900,8 @@ TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleToSelf) {
}
void SendSharedMemoryHandleToSelfCallback(IPC::Sender* sender,
- const IPC::Message&) {
+ const IPC::Message&,
+ ChildProcessGlobals* globals) {
// Do nothing special. The default behavior already runs the
// AttachmentBrokerPrivilegedMac.
}
@@ -937,7 +955,8 @@ TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleChannelProxy) {
}
void SendSharedMemoryHandleChannelProxyCallback(IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
bool success = CheckContentsOfMessage1(message, kDataBuffer1);
SendControlMessage(sender, success);
}
@@ -971,7 +990,9 @@ TEST_F(IPCAttachmentBrokerMacTest, ShareToProcess) {
CommonTearDown();
}
-void ShareToProcessCallback(IPC::Sender* sender, const IPC::Message& message) {
+void ShareToProcessCallback(IPC::Sender* sender,
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
bool success = CheckContentsOfMessage1(message, kDataBuffer1);
SendControlMessage(sender, success);
}
@@ -1004,7 +1025,8 @@ TEST_F(IPCAttachmentBrokerMacTest, ShareReadOnlyToProcess) {
}
void ShareReadOnlyToProcessCallback(IPC::Sender* sender,
- const IPC::Message& message) {
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message));
// Try to map the memory as writable.
@@ -1032,4 +1054,102 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ShareReadOnlyToProcess) {
"ShareReadOnlyToProcess");
}
+// Similar to SendSharedMemoryHandleToSelf, but the child process pretends to
+// not have the task port for the parent process.
+TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleToSelfDelayedPort) {
+ // Mach-based SharedMemory isn't support on OSX 10.6.
+ if (base::mac::IsOSSnowLeopard())
+ return;
+
+ SetBroker(new MockBroker);
+ CommonSetUp("SendSharedMemoryHandleToSelfDelayedPort");
+
+ // Technically, the channel is an endpoint, but we need the proxy listener to
+ // receive the messages so that it can quit the message loop.
+ channel()->SetAttachmentBrokerEndpoint(false);
+ get_proxy_listener()->set_listener(get_broker());
+
+ {
+ scoped_ptr<base::SharedMemory> shared_memory(
+ MakeSharedMemory(kDataBuffer1));
+ mach_port_urefs_t ref_count = IPC::GetMachRefCount(
+ shared_memory->handle().GetMemoryObject(), MACH_PORT_RIGHT_SEND);
+
+ std::vector<IPC::BrokerableAttachment::AttachmentId> ids;
+ const int kMessagesToTest = 3;
+ for (int i = 0; i < kMessagesToTest; ++i) {
+ base::SharedMemoryHandle h = shared_memory->handle().Duplicate();
+ ids.push_back(
+ IPC::BrokerableAttachment::AttachmentId::CreateIdWithRandomNonce());
+ IPC::internal::MachPortAttachmentMac::WireFormat wire_format(
+ h.GetMemoryObject(), getpid(), ids[i]);
+ sender()->Send(new AttachmentBrokerMsg_DuplicateMachPort(wire_format));
+
+ // Send a dummy message, which will trigger the callback handler in the
+ // child process.
+ sender()->Send(new TestSharedMemoryHandleMsg4(1));
+ }
+
+ int received_message_count = 0;
+ while (received_message_count < kMessagesToTest) {
+ // Wait until the child process has sent this process a message.
+ base::MessageLoop::current()->Run();
+
+ // Wait for any asynchronous activity to complete.
+ base::MessageLoop::current()->RunUntilIdle();
+
+ while (get_proxy_listener()->has_message()) {
+ get_proxy_listener()->pop_first_message();
+ received_message_count++;
+ }
+ }
+
+ for (int i = 0; i < kMessagesToTest; ++i) {
+ IPC::BrokerableAttachment::AttachmentId* id = &ids[i];
+ ASSERT_TRUE(id);
+ scoped_refptr<IPC::BrokerableAttachment> received_attachment;
+ get_broker()->GetAttachmentWithId(*id, &received_attachment);
+ ASSERT_NE(received_attachment.get(), nullptr);
+
+ base::mac::ScopedMachSendRight memory_object(
+ GetMachPortFromBrokeredAttachment(received_attachment));
+ ASSERT_EQ(shared_memory->handle().GetMemoryObject(), memory_object);
+ }
+
+ // Check that the ref count hasn't changed.
+ EXPECT_EQ(ref_count,
+ IPC::GetMachRefCount(shared_memory->handle().GetMemoryObject(),
+ MACH_PORT_RIGHT_SEND));
+ }
+
+ FinalCleanUp();
+}
+
+void SendSharedMemoryHandleToSelfDelayedPortCallback(
+ IPC::Sender* sender,
+ const IPC::Message& message,
+ ChildProcessGlobals* globals) {
+ static int i = 0;
+ static base::ProcessId pid = message.get_sender_pid();
+ static mach_port_t task_port = globals->port_provider.TaskForPid(pid);
+ ++i;
+
+ if (i == 1) {
+ // Pretend to not have the task port for the parent.
+ globals->port_provider.ClearPortMap();
+ } else if (i == 2) {
+ // Intentionally do nothing.
+ } else if (i == 3) {
+ // Setting the task port should trigger callbacks, eventually resulting in
+ // multiple attachment broker messages.
+ globals->port_provider.InsertEntry(pid, task_port);
+ }
+}
+
+MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelfDelayedPort) {
+ return CommonPrivilegedProcessMain(
+ &SendSharedMemoryHandleToSelfDelayedPortCallback,
+ "SendSharedMemoryHandleToSelfDelayedPort");
+}
+
} // namespace
diff --git a/ipc/attachment_broker_privileged.h b/ipc/attachment_broker_privileged.h
index 5520af2..5a55e67 100644
--- a/ipc/attachment_broker_privileged.h
+++ b/ipc/attachment_broker_privileged.h
@@ -71,12 +71,14 @@ class IPC_EXPORT AttachmentBrokerPrivileged : public IPC::AttachmentBroker {
ERROR_MAKE_RECEIVE_PORT = 6,
// Couldn't change the attributes of a Mach port.
ERROR_SET_ATTRIBUTES = 7,
- // Couldn't extract a right.
- ERROR_EXTRACT_RIGHT = 8,
+ // Couldn't extract a right from the destination.
+ ERROR_EXTRACT_DEST_RIGHT = 8,
// Couldn't send a Mach port in a call to mach_msg().
ERROR_SEND_MACH_PORT = 9,
// Couldn't decrease the ref count on a Mach port.
ERROR_DECREASE_REF = 10,
+ // Couldn't extract a right from the source.
+ ERROR_EXTRACT_SOURCE_RIGHT = 11,
ERROR_MAX
};
diff --git a/ipc/attachment_broker_privileged_mac.cc b/ipc/attachment_broker_privileged_mac.cc
index 8965eb5..16dbff4 100644
--- a/ipc/attachment_broker_privileged_mac.cc
+++ b/ipc/attachment_broker_privileged_mac.cc
@@ -55,9 +55,23 @@ namespace IPC {
AttachmentBrokerPrivilegedMac::AttachmentBrokerPrivilegedMac(
base::PortProvider* port_provider)
- : port_provider_(port_provider) {}
+ : port_provider_(port_provider) {
+ port_provider_->AddObserver(this);
+}
-AttachmentBrokerPrivilegedMac::~AttachmentBrokerPrivilegedMac() {}
+AttachmentBrokerPrivilegedMac::~AttachmentBrokerPrivilegedMac() {
+ port_provider_->RemoveObserver(this);
+ {
+ base::AutoLock l(precursors_lock_);
+ for (auto it : precursors_)
+ delete it.second;
+ }
+ {
+ base::AutoLock l(extractors_lock_);
+ for (auto it : extractors_)
+ delete it.second;
+ }
+}
bool AttachmentBrokerPrivilegedMac::SendAttachmentToProcess(
const scoped_refptr<IPC::BrokerableAttachment>& attachment,
@@ -68,25 +82,11 @@ bool AttachmentBrokerPrivilegedMac::SendAttachmentToProcess(
static_cast<internal::MachPortAttachmentMac*>(attachment.get());
MachPortWireFormat wire_format =
mach_port_attachment->GetWireFormat(destination_process);
-
- if (destination_process == base::Process::Current().Pid()) {
- RouteWireFormatToSelf(wire_format);
- mach_port_attachment->reset_mach_port_ownership();
- return true;
- }
-
- mach_port_name_t intermediate_port = CreateIntermediateMachPort(
- wire_format.destination_process,
- base::mac::ScopedMachSendRight(wire_format.mach_port));
+ AddPrecursor(wire_format.destination_process,
+ base::mac::ScopedMachSendRight(wire_format.mach_port),
+ wire_format.attachment_id);
mach_port_attachment->reset_mach_port_ownership();
- if (intermediate_port == MACH_PORT_NULL) {
- LogError(ERROR_MAKE_INTERMEDIATE);
- return false;
- }
-
- MachPortWireFormat intermediate_wire_format =
- CopyWireFormat(wire_format, intermediate_port);
- RouteWireFormatToAnother(intermediate_wire_format);
+ SendPrecursorsForProcess(wire_format.destination_process);
return true;
}
default:
@@ -106,8 +106,39 @@ bool AttachmentBrokerPrivilegedMac::OnMessageReceived(const Message& msg) {
return handled;
}
+void AttachmentBrokerPrivilegedMac::OnReceivedTaskPort(
+ base::ProcessHandle process) {
+ SendPrecursorsForProcess(process);
+}
+
+AttachmentBrokerPrivilegedMac::AttachmentPrecursor::AttachmentPrecursor(
+ const base::ProcessId& pid,
+ base::mac::ScopedMachSendRight port,
+ const BrokerableAttachment::AttachmentId& id)
+ : pid_(pid), port_(port.release()), id_(id) {}
+
+AttachmentBrokerPrivilegedMac::AttachmentPrecursor::~AttachmentPrecursor() {}
+
+base::mac::ScopedMachSendRight
+AttachmentBrokerPrivilegedMac::AttachmentPrecursor::TakePort() {
+ return base::mac::ScopedMachSendRight(port_.release());
+}
+
+AttachmentBrokerPrivilegedMac::AttachmentExtractor::AttachmentExtractor(
+ const base::ProcessId& source_pid,
+ const base::ProcessId& dest_pid,
+ mach_port_name_t port,
+ const BrokerableAttachment::AttachmentId& id)
+ : source_pid_(source_pid),
+ dest_pid_(dest_pid),
+ port_to_extract_(port),
+ id_(id) {}
+
+AttachmentBrokerPrivilegedMac::AttachmentExtractor::~AttachmentExtractor() {}
+
void AttachmentBrokerPrivilegedMac::OnDuplicateMachPort(
const IPC::Message& message) {
+ DCHECK_NE(0, message.get_sender_pid());
AttachmentBrokerMsg_DuplicateMachPort::Param param;
if (!AttachmentBrokerMsg_DuplicateMachPort::Read(&message, &param)) {
LogError(ERROR_PARSE_DUPLICATE_MACH_PORT_MESSAGE);
@@ -121,31 +152,18 @@ void AttachmentBrokerPrivilegedMac::OnDuplicateMachPort(
return;
}
- // Acquire a send right to the Mach port.
- base::ProcessId sender_pid = message.get_sender_pid();
- DCHECK_NE(sender_pid, base::GetCurrentProcId());
- base::mac::ScopedMachSendRight send_right(
- AcquireSendRight(sender_pid, wire_format.mach_port));
-
- if (wire_format.destination_process == base::GetCurrentProcId()) {
- // Intentionally leak the reference, as the consumer of the Chrome IPC
- // message will take ownership.
- mach_port_t final_mach_port = send_right.release();
- MachPortWireFormat final_wire_format(
- CopyWireFormat(wire_format, final_mach_port));
- RouteWireFormatToSelf(final_wire_format);
- return;
- }
-
- mach_port_name_t intermediate_mach_port = CreateIntermediateMachPort(
- wire_format.destination_process,
- base::mac::ScopedMachSendRight(send_right.release()));
- RouteWireFormatToAnother(CopyWireFormat(wire_format, intermediate_mach_port));
+ AddExtractor(message.get_sender_pid(), wire_format.destination_process,
+ wire_format.mach_port, wire_format.attachment_id);
+ ProcessExtractorsForProcess(message.get_sender_pid());
}
-void AttachmentBrokerPrivilegedMac::RouteWireFormatToSelf(
- const MachPortWireFormat& wire_format) {
- DCHECK_EQ(wire_format.destination_process, base::Process::Current().Pid());
+void AttachmentBrokerPrivilegedMac::RoutePrecursorToSelf(
+ AttachmentPrecursor* precursor) {
+ DCHECK_EQ(base::Process::Current().Pid(), precursor->pid());
+
+ // Intentionally leak the port, since the attachment takes ownership.
+ internal::MachPortAttachmentMac::WireFormat wire_format(
+ precursor->TakePort().release(), precursor->pid(), precursor->id());
scoped_refptr<BrokerableAttachment> attachment(
new internal::MachPortAttachmentMac(wire_format));
HandleReceivedAttachment(attachment);
@@ -173,19 +191,6 @@ void AttachmentBrokerPrivilegedMac::RouteWireFormatToAnother(
}
mach_port_name_t AttachmentBrokerPrivilegedMac::CreateIntermediateMachPort(
- base::ProcessId pid,
- base::mac::ScopedMachSendRight port_to_insert) {
- DCHECK_NE(pid, base::GetCurrentProcId());
- mach_port_t task_port = port_provider_->TaskForPid(pid);
- if (task_port == MACH_PORT_NULL) {
- LogError(ERROR_TASK_FOR_PID);
- return MACH_PORT_NULL;
- }
- return CreateIntermediateMachPort(
- task_port, base::mac::ScopedMachSendRight(port_to_insert.release()));
-}
-
-mach_port_name_t AttachmentBrokerPrivilegedMac::CreateIntermediateMachPort(
mach_port_t task_port,
base::mac::ScopedMachSendRight port_to_insert) {
DCHECK_NE(mach_task_self(), task_port);
@@ -219,7 +224,7 @@ mach_port_name_t AttachmentBrokerPrivilegedMac::CreateIntermediateMachPort(
mach_port_extract_right(task_port, endpoint, MACH_MSG_TYPE_MAKE_SEND_ONCE,
&send_once_right, &send_right_type);
if (kr != KERN_SUCCESS) {
- LogError(ERROR_EXTRACT_RIGHT);
+ LogError(ERROR_EXTRACT_DEST_RIGHT);
mach_port_deallocate(task_port, endpoint);
return MACH_PORT_NULL;
}
@@ -263,8 +268,10 @@ base::mac::ScopedMachSendRight AttachmentBrokerPrivilegedMac::ExtractNamedRight(
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)
+ if (kr != KERN_SUCCESS) {
+ LogError(ERROR_EXTRACT_SOURCE_RIGHT);
return base::mac::ScopedMachSendRight(MACH_PORT_NULL);
+ }
DCHECK_EQ(static_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND),
extracted_right_type);
@@ -288,4 +295,108 @@ AttachmentBrokerPrivilegedMac::CopyWireFormat(
wire_format.attachment_id);
}
+void AttachmentBrokerPrivilegedMac::SendPrecursorsForProcess(
+ base::ProcessId pid) {
+ base::AutoLock l(precursors_lock_);
+ auto it = precursors_.find(pid);
+ if (it == precursors_.end())
+ return;
+
+ // Whether this process is the destination process.
+ bool to_self = pid == base::GetCurrentProcId();
+
+ mach_port_t task_port = port_provider_->TaskForPid(pid);
+ if (!to_self && task_port == MACH_PORT_NULL)
+ return;
+
+ while (!it->second->empty()) {
+ auto precursor_it = it->second->begin();
+ if (to_self)
+ RoutePrecursorToSelf(*precursor_it);
+ else
+ SendPrecursor(*precursor_it, task_port);
+ it->second->erase(precursor_it);
+ }
+
+ delete it->second;
+ precursors_.erase(it);
+}
+
+void AttachmentBrokerPrivilegedMac::SendPrecursor(
+ AttachmentPrecursor* precursor,
+ mach_port_t task_port) {
+ DCHECK(task_port);
+ internal::MachPortAttachmentMac::WireFormat wire_format(
+ MACH_PORT_NULL, precursor->pid(), precursor->id());
+ base::mac::ScopedMachSendRight port_to_insert = precursor->TakePort();
+ mach_port_name_t intermediate_port = MACH_PORT_NULL;
+ if (port_to_insert.get() != MACH_PORT_NULL) {
+ intermediate_port = CreateIntermediateMachPort(
+ task_port, base::mac::ScopedMachSendRight(port_to_insert.release()));
+ }
+ RouteWireFormatToAnother(CopyWireFormat(wire_format, intermediate_port));
+}
+
+void AttachmentBrokerPrivilegedMac::AddPrecursor(
+ base::ProcessId pid,
+ base::mac::ScopedMachSendRight port,
+ const BrokerableAttachment::AttachmentId& id) {
+ base::AutoLock l(precursors_lock_);
+ auto it = precursors_.find(pid);
+ if (it == precursors_.end())
+ precursors_[pid] = new ScopedVector<AttachmentPrecursor>;
+
+ precursors_[pid]->push_back(new AttachmentPrecursor(
+ pid, base::mac::ScopedMachSendRight(port.release()), id));
+}
+
+void AttachmentBrokerPrivilegedMac::ProcessExtractorsForProcess(
+ base::ProcessId pid) {
+ base::AutoLock l(extractors_lock_);
+ auto it = extractors_.find(pid);
+ if (it == extractors_.end())
+ return;
+
+ mach_port_t task_port = port_provider_->TaskForPid(pid);
+ if (task_port == MACH_PORT_NULL)
+ return;
+
+ while (!it->second->empty()) {
+ auto extractor_it = it->second->begin();
+ ProcessExtractor(*extractor_it, task_port);
+ it->second->erase(extractor_it);
+ }
+
+ delete it->second;
+ extractors_.erase(it);
+}
+
+void AttachmentBrokerPrivilegedMac::ProcessExtractor(
+ AttachmentExtractor* extractor,
+ mach_port_t task_port) {
+ DCHECK(task_port);
+ base::mac::ScopedMachSendRight send_right =
+ ExtractNamedRight(task_port, extractor->port());
+ AddPrecursor(extractor->dest_pid(),
+ base::mac::ScopedMachSendRight(send_right.release()),
+ extractor->id());
+ SendPrecursorsForProcess(extractor->dest_pid());
+}
+
+void AttachmentBrokerPrivilegedMac::AddExtractor(
+ base::ProcessId source_pid,
+ base::ProcessId dest_pid,
+ mach_port_name_t port,
+ const BrokerableAttachment::AttachmentId& id) {
+ base::AutoLock l(extractors_lock_);
+ DCHECK_NE(base::GetCurrentProcId(), source_pid);
+
+ auto it = extractors_.find(source_pid);
+ if (it == extractors_.end())
+ extractors_[source_pid] = new ScopedVector<AttachmentExtractor>;
+
+ extractors_[source_pid]->push_back(
+ new AttachmentExtractor(source_pid, dest_pid, port, id));
+}
+
} // namespace IPC
diff --git a/ipc/attachment_broker_privileged_mac.h b/ipc/attachment_broker_privileged_mac.h
index e54d846..36abaf3 100644
--- a/ipc/attachment_broker_privileged_mac.h
+++ b/ipc/attachment_broker_privileged_mac.h
@@ -7,9 +7,14 @@
#include <mach/mach.h>
+#include <map>
+
#include "base/gtest_prod_util.h"
#include "base/mac/scoped_mach_port.h"
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
#include "base/process/port_provider_mac.h"
+#include "base/synchronization/lock.h"
#include "ipc/attachment_broker_privileged.h"
#include "ipc/ipc_export.h"
#include "ipc/mach_port_attachment_mac.h"
@@ -49,7 +54,8 @@ namespace IPC {
// For the rest of this file, and the corresponding implementation file, R will
// be called the "intermediate Mach port" and M3 the "final Mach port".
class IPC_EXPORT AttachmentBrokerPrivilegedMac
- : public AttachmentBrokerPrivileged {
+ : public AttachmentBrokerPrivileged,
+ public base::PortProvider::Observer {
public:
explicit AttachmentBrokerPrivilegedMac(base::PortProvider* port_provider);
~AttachmentBrokerPrivilegedMac() override;
@@ -62,6 +68,9 @@ class IPC_EXPORT AttachmentBrokerPrivilegedMac
// IPC::Listener overrides.
bool OnMessageReceived(const Message& message) override;
+ // base::PortProvider::Observer override.
+ void OnReceivedTaskPort(base::ProcessHandle process) override;
+
private:
FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
InsertRight);
@@ -70,6 +79,58 @@ class IPC_EXPORT AttachmentBrokerPrivilegedMac
FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest,
InsertTwoRights);
using MachPortWireFormat = internal::MachPortAttachmentMac::WireFormat;
+
+ // Contains all the information necessary to broker an attachment into a
+ // destination process. The only thing that prevents an AttachmentPrecusor
+ // from being immediately processed is if |port_provider_| does not yet have a
+ // task port for |pid|.
+ class IPC_EXPORT AttachmentPrecursor {
+ public:
+ AttachmentPrecursor(const base::ProcessId& pid,
+ base::mac::ScopedMachSendRight port_to_insert,
+ const BrokerableAttachment::AttachmentId& id);
+ ~AttachmentPrecursor();
+
+ // Caller takes ownership of |port_|.
+ base::mac::ScopedMachSendRight TakePort();
+
+ base::ProcessId pid() const { return pid_; }
+ const BrokerableAttachment::AttachmentId id() const { return id_; }
+
+ private:
+ // The pid of the destination process.
+ const base::ProcessId pid_;
+ // The final Mach port, as per definition at the top of this file.
+ base::mac::ScopedMachSendRight port_;
+ // The id of the attachment.
+ const BrokerableAttachment::AttachmentId id_;
+ DISALLOW_COPY_AND_ASSIGN(AttachmentPrecursor);
+ };
+
+ // Contains all the information necessary to extract a send right and create
+ // an AttachmentPrecursor. The only thing that prevents an AttachmentExtractor
+ // from being immediately processed is if |port_provider_| does not yet have a
+ // task port for |source_pid|.
+ class IPC_EXPORT AttachmentExtractor {
+ public:
+ AttachmentExtractor(const base::ProcessId& source_pid,
+ const base::ProcessId& dest_pid,
+ mach_port_name_t port,
+ const BrokerableAttachment::AttachmentId& id);
+ ~AttachmentExtractor();
+
+ base::ProcessId source_pid() const { return source_pid_; }
+ base::ProcessId dest_pid() const { return dest_pid_; }
+ mach_port_name_t port() const { return port_to_extract_; }
+ const BrokerableAttachment::AttachmentId id() const { return id_; }
+
+ private:
+ const base::ProcessId source_pid_;
+ const base::ProcessId dest_pid_;
+ mach_port_name_t port_to_extract_;
+ const BrokerableAttachment::AttachmentId id_;
+ };
+
// IPC message handlers.
void OnDuplicateMachPort(const Message& message);
@@ -78,20 +139,14 @@ class IPC_EXPORT AttachmentBrokerPrivilegedMac
MachPortWireFormat DuplicateMachPort(const MachPortWireFormat& wire_format,
base::ProcessId source_process);
- // |pid| must be another process.
+ // |task_port| is the task port of another process.
// |port_to_insert| must be a send right in the current task's name space.
// Creates an intermediate Mach port in |pid| and sends |port_to_insert| as a
// mach_msg to the intermediate Mach port.
- // On success, returns the name of the intermediate Mach port.
- // On failure, returns |MACH_PORT_NULL|.
+ // Returns the intermediate port on success, and MACH_PORT_NULL on failure.
// This method takes ownership of |port_to_insert|. On success, ownership is
// passed to the intermediate Mach port.
mach_port_name_t CreateIntermediateMachPort(
- base::ProcessId pid,
- base::mac::ScopedMachSendRight port_to_insert);
-
- // Same as the above method, where |task_port| is the task port of |pid|.
- mach_port_name_t CreateIntermediateMachPort(
mach_port_t task_port,
base::mac::ScopedMachSendRight port_to_insert);
@@ -115,7 +170,7 @@ class IPC_EXPORT AttachmentBrokerPrivilegedMac
// Consumes a reference to |wire_format.mach_port|, as ownership is implicitly
// passed to the consumer of the Chrome IPC message.
// Makes an attachment, queues it, and notifies the observers.
- void RouteWireFormatToSelf(const MachPortWireFormat& wire_format);
+ void RoutePrecursorToSelf(AttachmentPrecursor* precursor);
// |wire_format.destination_process| must be another process.
// |wire_format.mach_port| must be the intermediate Mach port.
@@ -123,8 +178,44 @@ class IPC_EXPORT AttachmentBrokerPrivilegedMac
// that receives the Chrome IPC message.
void RouteWireFormatToAnother(const MachPortWireFormat& wire_format);
+ // Atempts to broker all precursors whose destination is |pid|. Has no effect
+ // if |port_provider_| does not have the task port for |pid|.
+ void SendPrecursorsForProcess(base::ProcessId pid);
+
+ // Brokers a single precursor into the task represented by |task_port|.
+ void SendPrecursor(AttachmentPrecursor* precursor, mach_port_t task_port);
+
+ // Add a precursor to |precursors_|. Takes ownership of |port|.
+ void AddPrecursor(base::ProcessId pid,
+ base::mac::ScopedMachSendRight port,
+ const BrokerableAttachment::AttachmentId& id);
+
+ // Atempts to process all extractors whose source is |pid|. Has no effect
+ // if |port_provider_| does not have the task port for |pid|.
+ void ProcessExtractorsForProcess(base::ProcessId pid);
+
+ // Processes a single extractor whose source pid is represented by
+ // |task_port|.
+ void ProcessExtractor(AttachmentExtractor* extractor, mach_port_t task_port);
+
+ // Add an extractor to |extractors_|.
+ void AddExtractor(base::ProcessId source_pid,
+ base::ProcessId dest_pid,
+ mach_port_name_t port,
+ const BrokerableAttachment::AttachmentId& id);
+
// The port provider must live at least as long as the AttachmentBroker.
- const base::PortProvider* port_provider_;
+ base::PortProvider* port_provider_;
+
+ // For each ProcessId, a vector of precursors that are waiting to be
+ // sent.
+ std::map<base::ProcessId, ScopedVector<AttachmentPrecursor>*> precursors_;
+ base::Lock precursors_lock_;
+
+ // For each ProcessId, a vector of extractors that are waiting to be
+ // processed.
+ std::map<base::ProcessId, ScopedVector<AttachmentExtractor>*> extractors_;
+ base::Lock extractors_lock_;
DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerPrivilegedMac);
};
diff --git a/ipc/attachment_broker_privileged_mac_unittest.cc b/ipc/attachment_broker_privileged_mac_unittest.cc
index 07222a3..3ad2a9d 100644
--- a/ipc/attachment_broker_privileged_mac_unittest.cc
+++ b/ipc/attachment_broker_privileged_mac_unittest.cc
@@ -119,6 +119,15 @@ scoped_ptr<base::SharedMemory> MapMemoryObject(mach_port_t memory_object,
return shared_memory;
}
+class MockPortProvider : public base::PortProvider {
+ public:
+ MockPortProvider() {}
+ ~MockPortProvider() override {}
+ mach_port_t TaskForPid(base::ProcessHandle process) const override {
+ return MACH_PORT_NULL;
+ }
+};
+
} // namespace
class AttachmentBrokerPrivilegedMacMultiProcessTest
@@ -159,6 +168,9 @@ class AttachmentBrokerPrivilegedMacMultiProcessTest
// Child process's task port.
base::mac::ScopedMachSendRight client_task_port_;
+ // Dummy port provider.
+ MockPortProvider port_provider_;
+
base::Process child_process_;
DISALLOW_COPY_AND_ASSIGN(AttachmentBrokerPrivilegedMacMultiProcessTest);
};
@@ -172,7 +184,7 @@ TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertRight) {
SetUpChild("InsertRightClient");
mach_msg_type_number_t original_name_count = GetActiveNameCount();
- IPC::AttachmentBrokerPrivilegedMac broker(nullptr);
+ IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
// Create some shared memory.
scoped_ptr<base::SharedMemory> shared_memory =
@@ -239,7 +251,7 @@ TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertSameRightTwice) {
SetUpChild("InsertSameRightTwiceClient");
mach_msg_type_number_t original_name_count = GetActiveNameCount();
- IPC::AttachmentBrokerPrivilegedMac broker(nullptr);
+ IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
// Create some shared memory.
scoped_ptr<base::SharedMemory> shared_memory =
@@ -335,7 +347,7 @@ TEST_F(AttachmentBrokerPrivilegedMacMultiProcessTest, InsertTwoRights) {
SetUpChild("InsertTwoRightsClient");
mach_msg_type_number_t original_name_count = GetActiveNameCount();
- IPC::AttachmentBrokerPrivilegedMac broker(nullptr);
+ IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_);
for (int i = 0; i < 2; ++i) {
// Create some shared memory.
diff --git a/ipc/ipc_channel_reader.cc b/ipc/ipc_channel_reader.cc
index f03f002..e1cdc47 100644
--- a/ipc/ipc_channel_reader.cc
+++ b/ipc/ipc_channel_reader.cc
@@ -163,7 +163,6 @@ bool ChannelReader::TranslateInputData(const char* input_data,
bool ChannelReader::HandleTranslatedMessage(
Message* translated_message,
const AttachmentIdVector& attachment_ids) {
-
// Immediately handle internal messages.
if (IsInternalMessage(*translated_message)) {
EmitLogBeforeDispatch(*translated_message);
diff --git a/ipc/ipc_test_messages.h b/ipc/ipc_test_messages.h
index d1247cb..62af913 100644
--- a/ipc/ipc_test_messages.h
+++ b/ipc/ipc_test_messages.h
@@ -31,4 +31,6 @@ IPC_MESSAGE_CONTROL4(TestSharedMemoryHandleMsg3,
base::SharedMemoryHandle,
base::FileDescriptor,
base::SharedMemoryHandle)
+IPC_MESSAGE_CONTROL1(TestSharedMemoryHandleMsg4, int)
+
#endif // defined(OS_MACOSX)
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 8de26dd..023532a 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -64600,13 +64600,18 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
<int value="7" label="ERROR_SET_ATTRIBUTES">
Couldn't change the attributes of a Mach port.
</int>
- <int value="8" label="ERROR_EXTRACT_RIGHT">Couldn't extract a right.</int>
+ <int value="8" label="ERROR_EXTRACT_RIGHT_DEST">
+ Couldn't extract a right from the destination process.
+ </int>
<int value="9" label="ERROR_SEND_MACH_PORT">
Couldn't send a Mach port in a call to mach_msg().
</int>
<int value="10" label="ERROR_DECREASE_REF">
Couldn't decrease the ref count on a Mach port.
</int>
+ <int value="11" label="ERROR_EXTRACT_RIGHT_SOURCE">
+ Couldn't extract a right from the source process.
+ </int>
</enum>
<enum name="IPCAttachmentBrokerUnprivilegedBrokerAttachmentError" type="int">