diff options
-rw-r--r-- | base/base.gypi | 1 | ||||
-rw-r--r-- | base/process/BUILD.gn | 1 | ||||
-rw-r--r-- | base/process/port_provider_mac.cc | 27 | ||||
-rw-r--r-- | base/process/port_provider_mac.h | 34 | ||||
-rw-r--r-- | ipc/attachment_broker_mac_unittest.cc | 152 | ||||
-rw-r--r-- | ipc/attachment_broker_privileged.h | 6 | ||||
-rw-r--r-- | ipc/attachment_broker_privileged_mac.cc | 227 | ||||
-rw-r--r-- | ipc/attachment_broker_privileged_mac.h | 113 | ||||
-rw-r--r-- | ipc/attachment_broker_privileged_mac_unittest.cc | 18 | ||||
-rw-r--r-- | ipc/ipc_channel_reader.cc | 1 | ||||
-rw-r--r-- | ipc/ipc_test_messages.h | 2 | ||||
-rw-r--r-- | tools/metrics/histograms/histograms.xml | 7 |
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, ¶m)) { 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"> |