diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-01-25 11:39:07 +0100 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2016-01-25 15:20:42 +0000 |
commit | 6c91641271e536ffaa88a1dff5127e42ee99a91e (patch) | |
tree | 703d9dd49602377ddc90cbf886aad37913f2496b /chromium/ipc | |
parent | b145b7fafd36f0c260d6a768c81fc14e32578099 (diff) | |
download | qtwebengine-chromium-6c91641271e536ffaa88a1dff5127e42ee99a91e.tar.gz |
BASELINE: Update Chromium to 49.0.2623.23
Also adds missing printing sources.
Change-Id: I3726b8f0c7d6751c9fc846096c571fadca7108cd
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/ipc')
114 files changed, 4794 insertions, 768 deletions
diff --git a/chromium/ipc/BUILD.gn b/chromium/ipc/BUILD.gn index 4327bd18057..1dddc13484c 100644 --- a/chromium/ipc/BUILD.gn +++ b/chromium/ipc/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/nacl/config.gni") import("//testing/test.gni") component("ipc") { @@ -11,14 +12,19 @@ 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", "brokerable_attachment.h", + "brokerable_attachment_mac.cc", "brokerable_attachment_win.cc", "handle_attachment_win.cc", "handle_attachment_win.h", @@ -61,7 +67,6 @@ component("ipc") { "ipc_message_start.h", "ipc_message_utils.cc", "ipc_message_utils.h", - "ipc_param_traits.h", "ipc_platform_file.cc", "ipc_platform_file.h", "ipc_platform_file_attachment_posix.cc", @@ -95,7 +100,7 @@ component("ipc") { "unix_domain_socket_util.h", ] - if (is_nacl) { + if (is_nacl && !is_nacl_nonsfi) { sources -= [ "ipc_channel.cc", "ipc_channel_posix.cc", @@ -108,12 +113,15 @@ component("ipc") { ] } - if (is_win || is_ios) { + if (is_win || is_ios || is_nacl_nonsfi) { sources -= [ "unix_domain_socket_util.cc" ] } defines = [ "IPC_IMPLEMENTATION" ] + public_deps = [ + ":param_traits", + ] deps = [ "//base", @@ -121,98 +129,105 @@ component("ipc") { "//base/third_party/dynamic_annotations", ] - if (is_win) { - # On windows HandleAttachmentWin needs to generate random IDs. + if (is_win || is_mac) { + # On Windows HandleAttachmentWin needs to generate random IDs. + # On Mac MachPortAttachmentMac needs to generate random IDs. deps += [ "//crypto" ] } } -# TODO(GYP): crbug.com/360936. Get this to build and run on Android. -if (!is_android) { - group("ipc_tests_run") { - testonly = true - deps = [ - ":ipc_tests", - ] - } +# This is provided as a separate target so other targets can provide param +# traits implementations without necessarily linking to all of IPC. +source_set("param_traits") { + public = [ + "ipc_param_traits.h", + ] +} - test("ipc_tests") { - sources = [ - "attachment_broker_privileged_win_unittest.cc", - "attachment_broker_unprivileged_win_unittest.cc", - "ipc_channel_posix_unittest.cc", - "ipc_channel_proxy_unittest.cc", - "ipc_channel_reader_unittest.cc", - "ipc_channel_unittest.cc", - "ipc_fuzzing_tests.cc", - "ipc_message_attachment_set_posix_unittest.cc", - "ipc_message_unittest.cc", - "ipc_message_utils_unittest.cc", - "ipc_send_fds_test.cc", - "ipc_sync_channel_unittest.cc", - "ipc_sync_message_unittest.cc", - "ipc_sync_message_unittest.h", - "ipc_test_message_generator.cc", - "ipc_test_message_generator.h", - "ipc_test_messages.h", - "sync_socket_unittest.cc", - "unix_domain_socket_util_unittest.cc", - ] +group("ipc_tests_run") { + testonly = true + deps = [ + ":ipc_tests", + ] +} - if (is_win || is_ios) { - sources -= [ "unix_domain_socket_util_unittest.cc" ] - } - - # TODO(brettw) hook up Android testing. - #if (is_android && gtest_target_type == "shared_library") { - # deps += "/testing/android/native_test.gyp:native_testNative_code" - #} - - # TODO(brettw) hook up tcmalloc to this target. - #if (is_posix && !is_mac && !is_android) { - # if (use_allocator!="none") { - # deps += "/base/allocator" - # } - #} - - deps = [ - ":ipc", - ":test_support", - "//base", - "//base:i18n", - "//base/test:run_all_unittests", - "//base/test:test_support", - "//crypto", - "//testing/gtest", - ] +test("ipc_tests") { + sources = [ + "attachment_broker_mac_unittest.cc", + "attachment_broker_privileged_mac_unittest.cc", + "attachment_broker_privileged_win_unittest.cc", + "ipc_channel_posix_unittest.cc", + "ipc_channel_proxy_unittest.cc", + "ipc_channel_reader_unittest.cc", + "ipc_channel_unittest.cc", + "ipc_fuzzing_tests.cc", + "ipc_message_attachment_set_posix_unittest.cc", + "ipc_message_unittest.cc", + "ipc_message_utils_unittest.cc", + "ipc_send_fds_test.cc", + "ipc_sync_channel_unittest.cc", + "ipc_sync_message_unittest.cc", + "ipc_sync_message_unittest.h", + "ipc_test_message_generator.cc", + "ipc_test_message_generator.h", + "ipc_test_messages.h", + "sync_socket_unittest.cc", + "unix_domain_socket_util_unittest.cc", + ] + + if (is_win || is_ios) { + sources -= [ "unix_domain_socket_util_unittest.cc" ] } - test("ipc_perftests") { - sources = [ - "ipc_perftests.cc", - ] + # TODO(brettw) hook up Android testing. + #if (is_android && gtest_target_type == "shared_library") { + # deps += "/testing/android/native_test.gyp:native_testNative_code" + #} - # TODO(brettw) hook up Android testing. - #if (is_android && gtest_target_type == "shared_library") { - # deps += "/testing/android/native_test.gyp:native_testNative_code" - #} - - # TODO(brettw) hook up tcmalloc to this target. - #if (is_posix && !is_mac && !is_android) { - # if (use_allocator!="none") { - # deps += "//base/allocator" - # } - #} - deps = [ - ":ipc", - ":test_support", - "//base", - "//base:i18n", - "//base/test:test_support", - "//base/test:test_support_perf", - "//testing/gtest", - ] - } + # TODO(brettw) hook up tcmalloc to this target. + #if (is_posix && !is_mac && !is_android) { + # if (use_allocator!="none") { + # deps += "/base/allocator" + # } + #} + + deps = [ + ":ipc", + ":test_support", + "//base", + "//base:i18n", + "//base/test:run_all_unittests", + "//base/test:test_support", + "//crypto", + "//testing/gtest", + ] +} + +test("ipc_perftests") { + sources = [ + "ipc_perftests.cc", + ] + + # TODO(brettw) hook up Android testing. + #if (is_android && gtest_target_type == "shared_library") { + # deps += "/testing/android/native_test.gyp:native_testNative_code" + #} + + # TODO(brettw) hook up tcmalloc to this target. + #if (is_posix && !is_mac && !is_android) { + # if (use_allocator!="none") { + # deps += "//base/allocator" + # } + #} + deps = [ + ":ipc", + ":test_support", + "//base", + "//base:i18n", + "//base/test:test_support", + "//base/test:test_support_perf", + "//testing/gtest", + ] } source_set("test_support") { @@ -230,9 +245,13 @@ source_set("test_support") { "ipc_test_channel_listener.h", "ipc_test_sink.cc", "ipc_test_sink.h", + "test_util_mac.cc", + "test_util_mac.h", ] - deps = [ + public_deps = [ ":ipc", + ] + deps = [ "//base", "//base/test:test_support", "//testing/gtest", diff --git a/chromium/ipc/attachment_broker.cc b/chromium/ipc/attachment_broker.cc index 2fc4fb4fe9c..50e3e2d9ae7 100644 --- a/chromium/ipc/attachment_broker.cc +++ b/chromium/ipc/attachment_broker.cc @@ -6,6 +6,10 @@ #include <algorithm> +#include "base/bind.h" +#include "base/location.h" +#include "base/sequenced_task_runner.h" + namespace { IPC::AttachmentBroker* g_attachment_broker = nullptr; } @@ -14,8 +18,9 @@ namespace IPC { // static void AttachmentBroker::SetGlobal(AttachmentBroker* broker) { - CHECK(!g_attachment_broker) - << "An attachment broker already exists with memory address: " << broker; + CHECK(!g_attachment_broker || !broker) + << "Global attachment broker address: " << broker + << ". New attachment broker address: " << broker; g_attachment_broker = broker; } @@ -24,12 +29,13 @@ AttachmentBroker* AttachmentBroker::GetGlobal() { return g_attachment_broker; } -AttachmentBroker::AttachmentBroker() {} +AttachmentBroker::AttachmentBroker() : last_unique_id_(0) {} AttachmentBroker::~AttachmentBroker() {} bool AttachmentBroker::GetAttachmentWithId( BrokerableAttachment::AttachmentId id, scoped_refptr<BrokerableAttachment>* out_attachment) { + base::AutoLock auto_lock(*get_lock()); for (AttachmentVector::iterator it = attachments_.begin(); it != attachments_.end(); ++it) { if ((*it)->GetIdentifier() == id) { @@ -41,31 +47,86 @@ bool AttachmentBroker::GetAttachmentWithId( return false; } -void AttachmentBroker::AddObserver(AttachmentBroker::Observer* observer) { - auto it = std::find(observers_.begin(), observers_.end(), observer); - if (it == observers_.end()) - observers_.push_back(observer); +void AttachmentBroker::AddObserver( + AttachmentBroker::Observer* observer, + const scoped_refptr<base::SequencedTaskRunner>& runner) { + base::AutoLock auto_lock(*get_lock()); + auto it = std::find_if(observers_.begin(), observers_.end(), + [observer](const ObserverInfo& info) { + return info.observer == observer; + }); + if (it == observers_.end()) { + ObserverInfo info; + info.observer = observer; + info.runner = runner; + info.unique_id = ++last_unique_id_; + observers_.push_back(info); + } } void AttachmentBroker::RemoveObserver(AttachmentBroker::Observer* observer) { - auto it = std::find(observers_.begin(), observers_.end(), observer); + base::AutoLock auto_lock(*get_lock()); + auto it = std::find_if(observers_.begin(), observers_.end(), + [observer](const ObserverInfo& info) { + return info.observer == observer; + }); if (it != observers_.end()) observers_.erase(it); } +void AttachmentBroker::RegisterCommunicationChannel(Endpoint* endpoint) { + NOTREACHED(); +} + +void AttachmentBroker::DeregisterCommunicationChannel(Endpoint* endpoint) { + NOTREACHED(); +} + void AttachmentBroker::HandleReceivedAttachment( const scoped_refptr<BrokerableAttachment>& attachment) { - attachments_.push_back(attachment); + { + base::AutoLock auto_lock(*get_lock()); + attachments_.push_back(attachment); + } NotifyObservers(attachment->GetIdentifier()); } void AttachmentBroker::NotifyObservers( const BrokerableAttachment::AttachmentId& id) { - // Make a copy of observers_ to avoid mutations during iteration. - std::vector<Observer*> observers = observers_; - for (Observer* observer : observers) { - observer->ReceivedBrokerableAttachmentWithId(id); + base::AutoLock auto_lock(*get_lock()); + + // Dispatch notifications onto the appropriate task runners. This has two + // effects: + // 1. Ensures that the notification is posted from the right task runner. + // 2. Avoids any complications from re-entrant functions, since one of the + // observers may be halfway through processing some messages. + for (const auto& info : observers_) { + info.runner->PostTask( + FROM_HERE, base::Bind(&AttachmentBroker::NotifyObserver, + base::Unretained(this), info.unique_id, id)); } } +void AttachmentBroker::NotifyObserver( + int unique_id, + const BrokerableAttachment::AttachmentId& id) { + Observer* observer = nullptr; + { + // Check that the same observer is still registered. + base::AutoLock auto_lock(*get_lock()); + auto it = std::find_if(observers_.begin(), observers_.end(), + [unique_id](const ObserverInfo& info) { + return info.unique_id == unique_id; + }); + if (it == observers_.end()) + return; + observer = it->observer; + } + + observer->ReceivedBrokerableAttachmentWithId(id); +} + +AttachmentBroker::ObserverInfo::ObserverInfo() {} +AttachmentBroker::ObserverInfo::~ObserverInfo() {} + } // namespace IPC diff --git a/chromium/ipc/attachment_broker.h b/chromium/ipc/attachment_broker.h index 577d76219f9..4cf1ab5de58 100644 --- a/chromium/ipc/attachment_broker.h +++ b/chromium/ipc/attachment_broker.h @@ -7,7 +7,10 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/process/process_handle.h" +#include "base/synchronization/lock.h" +#include "build/build_config.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_export.h" #include "ipc/ipc_listener.h" @@ -16,15 +19,21 @@ // compile any code that calls member functions of AttachmentBroker. This // prevents symbols only used by AttachmentBroker and its subclasses from // making it into the binary. -#if defined(OS_WIN) +#if defined(OS_WIN) || (defined(OS_MACOSX) && !defined(OS_IOS)) #define USE_ATTACHMENT_BROKER 1 #else #define USE_ATTACHMENT_BROKER 0 #endif // defined(OS_WIN) +namespace base { +class SequencedTaskRunner; +}; + namespace IPC { class AttachmentBroker; +class Endpoint; + // Classes that inherit from this abstract base class are capable of // communicating with a broker to send and receive attachments to Chrome IPC // messages. @@ -63,8 +72,9 @@ class IPC_EXPORT AttachmentBroker : public Listener { // IPC::Channel to communicate with the broker process. This may be the same // IPC::Channel that is requesting the brokering of an attachment. // Returns true on success and false otherwise. - virtual bool SendAttachmentToProcess(const BrokerableAttachment* attachment, - base::ProcessId destination_process) = 0; + virtual bool SendAttachmentToProcess( + const scoped_refptr<BrokerableAttachment>& attachment, + base::ProcessId destination_process) = 0; // Returns whether the attachment was available. If the attachment was // available, populates the output parameter |attachment|. @@ -72,9 +82,21 @@ class IPC_EXPORT AttachmentBroker : public Listener { scoped_refptr<BrokerableAttachment>* attachment); // Any given observer should only ever add itself once to the observer list. - void AddObserver(Observer* observer); + // Notifications to |observer| will be posted to |runner|. + // The |observer| is expected to call RemoveObserver() before being destroyed. + void AddObserver(Observer* observer, + const scoped_refptr<base::SequencedTaskRunner>& runner); void RemoveObserver(Observer* observer); + // These two methods should only be called by the broker process. + // + // Each unprivileged process should have one IPC channel on which it + // communicates attachment information with the broker process. In the broker + // process, these channels must be registered and deregistered with the + // Attachment Broker as they are created and destroyed. + virtual void RegisterCommunicationChannel(Endpoint* endpoint); + virtual void DeregisterCommunicationChannel(Endpoint* endpoint); + protected: using AttachmentVector = std::vector<scoped_refptr<BrokerableAttachment>>; @@ -85,9 +107,16 @@ class IPC_EXPORT AttachmentBroker : public Listener { // Informs the observers that a new BrokerableAttachment has been received. void NotifyObservers(const BrokerableAttachment::AttachmentId& id); + // Informs the observer identified by |unique_id| that a new + // BrokerableAttachment has been received. + void NotifyObserver(int unique_id, + const BrokerableAttachment::AttachmentId& id); + // This method is exposed for testing only. AttachmentVector* get_attachments() { return &attachments_; } + base::Lock* get_lock() { return &lock_; } + private: #if defined(OS_WIN) FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerUnprivilegedWinTest, @@ -103,7 +132,24 @@ class IPC_EXPORT AttachmentBroker : public Listener { // better performance. AttachmentVector attachments_; - std::vector<Observer*> observers_; + struct ObserverInfo { + ObserverInfo(); + ~ObserverInfo(); + + Observer* observer; + int unique_id; + + // Notifications must be dispatched onto |runner|. + scoped_refptr<base::SequencedTaskRunner> runner; + }; + std::vector<ObserverInfo> observers_; + + // This member holds the last id given to an ObserverInfo. + int last_unique_id_; + + // The AttachmentBroker can be accessed from any thread, so modifications to + // internal state must be guarded by a lock. + base::Lock lock_; DISALLOW_COPY_AND_ASSIGN(AttachmentBroker); }; diff --git a/chromium/ipc/attachment_broker_mac_unittest.cc b/chromium/ipc/attachment_broker_mac_unittest.cc new file mode 100644 index 00000000000..bf994b77db2 --- /dev/null +++ b/chromium/ipc/attachment_broker_mac_unittest.cc @@ -0,0 +1,1335 @@ +// 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 "build/build_config.h" + +#include <fcntl.h> +#include <mach/mach_vm.h> +#include <stddef.h> +#include <sys/mman.h> + +#include "base/command_line.h" +#include "base/files/file_util.h" +#include "base/files/scoped_file.h" +#include "base/files/scoped_temp_dir.h" +#include "base/mac/mac_util.h" +#include "base/mac/mach_logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/strings/string_number_conversions.h" +#include "base/synchronization/spin_wait.h" +#include "base/time/time.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" +#include "ipc/ipc_message.h" +#include "ipc/ipc_test_base.h" +#include "ipc/ipc_test_messages.h" +#include "ipc/test_util_mac.h" + +namespace { + +const char kDataBuffer1[] = "This is some test data to write to the file."; +const char kDataBuffer2[] = "The lazy dog and a fox."; +const char kDataBuffer3[] = "Two green bears but not a potato."; +const char kDataBuffer4[] = "Red potato is best potato."; +const std::string g_service_switch_name = "service_name"; +const size_t g_large_message_size = 8 * 1024 * 1024; +const int g_large_message_count = 1000; +const size_t g_medium_message_size = 512 * 1024; + +// Running the message loop is expected to increase the number of resident +// pages. The exact amount is non-deterministic, but for a simple test suite +// like this one, the increase is expected to be less than 1 MB. +const size_t g_expected_memory_increase = 1024 * 1024; + +enum TestResult { + RESULT_UNKNOWN, + RESULT_SUCCESS, + RESULT_FAILURE, +}; + +mach_vm_size_t GetResidentSize() { + task_basic_info_64 info; + mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT; + kern_return_t kr = task_info(mach_task_self(), TASK_BASIC_INFO_64, + reinterpret_cast<task_info_t>(&info), &count); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "Couldn't get resident size."; + + return info.resident_size; +} + +base::mac::ScopedMachSendRight GetMachPortFromBrokeredAttachment( + const scoped_refptr<IPC::BrokerableAttachment>& attachment) { + if (attachment->GetType() != + IPC::BrokerableAttachment::TYPE_BROKERABLE_ATTACHMENT) { + LOG(INFO) << "Attachment type not TYPE_BROKERABLE_ATTACHMENT."; + return base::mac::ScopedMachSendRight(MACH_PORT_NULL); + } + + if (attachment->GetBrokerableType() != IPC::BrokerableAttachment::MACH_PORT) { + LOG(INFO) << "Brokerable type not MACH_PORT."; + return base::mac::ScopedMachSendRight(MACH_PORT_NULL); + } + + IPC::internal::MachPortAttachmentMac* received_mach_port_attachment = + static_cast<IPC::internal::MachPortAttachmentMac*>(attachment.get()); + base::mac::ScopedMachSendRight send_right( + received_mach_port_attachment->get_mach_port()); + received_mach_port_attachment->reset_mach_port_ownership(); + return send_right; +} + +// Makes a Mach port backed SharedMemory region and fills it with |contents|. +scoped_ptr<base::SharedMemory> MakeSharedMemory(const std::string& contents) { + base::SharedMemoryHandle shm(contents.size()); + if (!shm.IsValid()) { + LOG(ERROR) << "Failed to make SharedMemoryHandle."; + return nullptr; + } + scoped_ptr<base::SharedMemory> shared_memory( + new base::SharedMemory(shm, false)); + shared_memory->Map(contents.size()); + memcpy(shared_memory->memory(), contents.c_str(), contents.size()); + return shared_memory; +} + +// |message| must be deserializable as a TestSharedMemoryHandleMsg1. +base::SharedMemoryHandle GetSharedMemoryHandleFromMsg1( + const IPC::Message& message) { + // Expect a message with a brokered attachment. + if (!message.HasBrokerableAttachments()) { + LOG(ERROR) << "Message missing brokerable attachment."; + return base::SharedMemoryHandle(); + } + + TestSharedMemoryHandleMsg1::Schema::Param p; + if (!TestSharedMemoryHandleMsg1::Read(&message, &p)) { + LOG(ERROR) << "Failed to deserialize message."; + return base::SharedMemoryHandle(); + } + + return base::get<1>(p); +} + +// |message| must be deserializable as a TestSharedMemoryHandleMsg2. Returns +// whether deserialization was successful. |handle1| and |handle2| are output +// variables populated on success. +bool GetSharedMemoryHandlesFromMsg2(const IPC::Message& message, + base::SharedMemoryHandle* handle1, + base::SharedMemoryHandle* handle2) { + // Expect a message with a brokered attachment. + if (!message.HasBrokerableAttachments()) { + LOG(ERROR) << "Message missing brokerable attachment."; + return false; + } + + TestSharedMemoryHandleMsg2::Schema::Param p; + if (!TestSharedMemoryHandleMsg2::Read(&message, &p)) { + LOG(ERROR) << "Failed to deserialize message."; + return false; + } + + *handle1 = base::get<0>(p); + *handle2 = base::get<1>(p); + return true; +} + +// Returns |nullptr| on error. +scoped_ptr<base::SharedMemory> MapSharedMemoryHandle( + const base::SharedMemoryHandle& shm, + bool read_only) { + if (!shm.IsValid()) { + LOG(ERROR) << "Invalid SharedMemoryHandle"; + return nullptr; + } + + size_t size; + if (!shm.GetSize(&size)) { + LOG(ERROR) << "Couldn't get size of SharedMemoryHandle"; + return nullptr; + } + + scoped_ptr<base::SharedMemory> shared_memory( + new base::SharedMemory(shm, read_only)); + shared_memory->Map(size); + return shared_memory; +} + +// This method maps the SharedMemoryHandle, checks the contents, and then +// consumes a reference to the underlying Mach port. +bool CheckContentsOfSharedMemoryHandle(const base::SharedMemoryHandle& shm, + const std::string& contents) { + scoped_ptr<base::SharedMemory> shared_memory( + MapSharedMemoryHandle(shm, false)); + + if (memcmp(shared_memory->memory(), contents.c_str(), contents.size()) != 0) { + LOG(ERROR) << "Shared Memory contents not equivalent"; + return false; + } + return true; +} + +// This method mmaps the FileDescriptor, checks the contents, and then munmaps +// the FileDescriptor and closes the underlying fd. +bool CheckContentsOfFileDescriptor(const base::FileDescriptor& file_descriptor, + const std::string& contents) { + base::ScopedFD fd_closer(file_descriptor.fd); + lseek(file_descriptor.fd, 0, SEEK_SET); + scoped_ptr<char, base::FreeDeleter> buffer( + static_cast<char*>(malloc(contents.size()))); + if (!base::ReadFromFD(file_descriptor.fd, buffer.get(), contents.size())) + return false; + + int result = memcmp(buffer.get(), contents.c_str(), contents.size()); + return result == 0; +} + +// Open |fp| and populate it with |contents|. +base::FileDescriptor MakeFileDescriptor(const base::FilePath& fp, + const std::string& contents) { + int fd = open(fp.value().c_str(), O_RDWR, S_IWUSR | S_IRUSR); + base::ScopedFD fd_closer(fd); + if (fd <= 0) { + LOG(ERROR) << "Error opening file at: " << fp.value(); + return base::FileDescriptor(); + } + + if (lseek(fd, 0, SEEK_SET) != 0) { + LOG(ERROR) << "Error changing offset"; + return base::FileDescriptor(); + } + + if (write(fd, contents.c_str(), contents.size()) != + static_cast<ssize_t>(contents.size())) { + LOG(ERROR) << "Error writing to file"; + return base::FileDescriptor(); + } + + return base::FileDescriptor(fd_closer.release(), true); +} + +// Maps both handles, then checks that their contents matches |contents|. Then +// checks that changes to one are reflected in the other. Then consumes +// references to both underlying Mach ports. +bool CheckContentsOfTwoEquivalentSharedMemoryHandles( + const base::SharedMemoryHandle& handle1, + const base::SharedMemoryHandle& handle2, + const std::string& contents) { + scoped_ptr<base::SharedMemory> shared_memory1( + MapSharedMemoryHandle(handle1, false)); + scoped_ptr<base::SharedMemory> shared_memory2( + MapSharedMemoryHandle(handle2, false)); + + if (memcmp(shared_memory1->memory(), contents.c_str(), contents.size()) != + 0) { + LOG(ERROR) << "Incorrect contents in shared_memory1"; + return false; + } + + if (memcmp(shared_memory1->memory(), shared_memory2->memory(), + contents.size()) != 0) { + LOG(ERROR) << "Incorrect contents in shared_memory2"; + return false; + } + + // Updating shared_memory1 should update shared_memory2. + const char known_string[] = "string bean"; + if (shared_memory1->mapped_size() < strlen(known_string) || + shared_memory2->mapped_size() < strlen(known_string)) { + LOG(ERROR) << "Shared memory size is too small"; + return false; + } + memcpy(shared_memory1->memory(), known_string, strlen(known_string)); + + if (memcmp(shared_memory1->memory(), shared_memory2->memory(), + strlen(known_string)) != 0) { + LOG(ERROR) << "Incorrect contents in shared_memory2"; + return false; + } + + return true; +} + +// |message| must be deserializable as a TestSharedMemoryHandleMsg1. Returns +// whether the contents of the attached shared memory region matches |contents|. +// Consumes a reference to the underlying Mach port. +bool CheckContentsOfMessage1(const IPC::Message& message, + const std::string& contents) { + base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message)); + return CheckContentsOfSharedMemoryHandle(shm, contents); +} + +// Once the test is finished, send a control message to the parent process with +// the result. The message may require the runloop to be run before its +// dispatched. +void SendControlMessage(IPC::Sender* sender, bool success) { + IPC::Message* message = new IPC::Message(0, 2, IPC::Message::PRIORITY_NORMAL); + TestResult result = success ? RESULT_SUCCESS : RESULT_FAILURE; + message->WriteInt(result); + sender->Send(message); +} + +// Records the most recently received brokerable attachment's id. +class AttachmentBrokerObserver : public IPC::AttachmentBroker::Observer { + public: + void ReceivedBrokerableAttachmentWithId( + const IPC::BrokerableAttachment::AttachmentId& id) override { + id_ = id; + } + IPC::BrokerableAttachment::AttachmentId* get_id() { return &id_; } + + private: + IPC::BrokerableAttachment::AttachmentId id_; +}; + +// A broker which always sets the current process as the destination process +// for attachments. +class MockBroker : public IPC::AttachmentBrokerUnprivilegedMac { + public: + MockBroker() {} + ~MockBroker() override {} + bool SendAttachmentToProcess( + const scoped_refptr<IPC::BrokerableAttachment>& attachment, + base::ProcessId destination_process) override { + return IPC::AttachmentBrokerUnprivilegedMac::SendAttachmentToProcess( + attachment, base::Process::Current().Pid()); + } +}; + +// Forwards all messages to |listener_|. Quits the message loop after a +// message is received, or the channel has an error. +class ProxyListener : public IPC::Listener { + public: + ProxyListener() : listener_(nullptr), reason_(MESSAGE_RECEIVED) {} + ~ProxyListener() override {} + + // The reason for exiting the message loop. + enum Reason { MESSAGE_RECEIVED, CHANNEL_ERROR }; + + bool OnMessageReceived(const IPC::Message& message) override { + bool result = false; + if (listener_) + result = listener_->OnMessageReceived(message); + reason_ = MESSAGE_RECEIVED; + messages_.push_back(message); + base::MessageLoop::current()->QuitNow(); + return result; + } + + void OnChannelError() override { + reason_ = CHANNEL_ERROR; + base::MessageLoop::current()->QuitNow(); + } + + void set_listener(IPC::Listener* listener) { listener_ = listener; } + Reason get_reason() { return reason_; } + IPC::Message get_first_message() { + DCHECK(!messages_.empty()); + return messages_[0]; + } + void pop_first_message() { + DCHECK(!messages_.empty()); + messages_.erase(messages_.begin()); + } + bool has_message() { return !messages_.empty(); } + + private: + IPC::Listener* listener_; + Reason reason_; + std::vector<IPC::Message> messages_; +}; + +// Waits for a result to be sent over the channel. Quits the message loop +// after a message is received, or the channel has an error. +class ResultListener : public IPC::Listener { + public: + ResultListener() : result_(RESULT_UNKNOWN) {} + ~ResultListener() override {} + + bool OnMessageReceived(const IPC::Message& message) override { + base::PickleIterator iter(message); + + int result; + EXPECT_TRUE(iter.ReadInt(&result)); + result_ = static_cast<TestResult>(result); + return true; + } + + TestResult get_result() { return result_; } + + private: + TestResult result_; +}; + +class MockPortProvider : public base::PortProvider { + public: + mach_port_t TaskForPid(base::ProcessHandle process) const override { + auto it = port_map_.find(process); + if (it != port_map_.end()) + return it->second; + return MACH_PORT_NULL; + } + + 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_; +}; + +// End-to-end tests for the attachment brokering process on Mac. +// The parent process acts as an unprivileged process. The child process acts +// as the privileged process. +class IPCAttachmentBrokerMacTest : public IPCTestBase { + public: + IPCAttachmentBrokerMacTest() {} + ~IPCAttachmentBrokerMacTest() override {} + + base::CommandLine MakeCmdLine(const std::string& procname) override { + base::CommandLine command_line = IPCTestBase::MakeCmdLine(procname); + // Pass the service name to the child process. + command_line.AppendSwitchASCII(g_service_switch_name, service_name_); + return command_line; + } + + // Takes ownership of |broker|. Has no effect if called after CommonSetUp(). + void SetBroker(IPC::AttachmentBrokerUnprivilegedMac* broker) { + broker_.reset(broker); + } + + // Mach Setup that needs to occur before child processes are forked. + void MachPreForkSetUp() { + service_name_ = IPC::CreateRandomServiceName(); + server_port_.reset(IPC::BecomeMachServer(service_name_.c_str()).release()); + } + + // Mach Setup that needs to occur after child processes are forked. + void MachPostForkSetUp() { + client_port_.reset(IPC::ReceiveMachPort(server_port_.get()).release()); + IPC::SendMachPort( + client_port_.get(), mach_task_self(), MACH_MSG_TYPE_COPY_SEND); + } + + // Setup shared between tests. + void CommonSetUp(const char* name) { + Init(name); + MachPreForkSetUp(); + + if (!broker_.get()) + SetBroker(new IPC::AttachmentBrokerUnprivilegedMac); + + broker_->AddObserver(&observer_, task_runner()); + CreateChannel(&proxy_listener_); + broker_->DesignateBrokerCommunicationChannel(channel()); + ASSERT_TRUE(ConnectChannel()); + ASSERT_TRUE(StartClient()); + + MachPostForkSetUp(); + active_names_at_start_ = IPC::GetActiveNameCount(); + get_proxy_listener()->set_listener(&result_listener_); + } + + void CheckChildResult() { + ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED, + get_proxy_listener()->get_reason()); + ASSERT_EQ(get_result_listener()->get_result(), RESULT_SUCCESS); + } + + void FinalCleanUp() { + // There should be no leaked names. + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE( + base::TimeDelta::FromSeconds(10), + active_names_at_start_ == IPC::GetActiveNameCount()); + EXPECT_EQ(active_names_at_start_, IPC::GetActiveNameCount()); + + // Close the channel so the client's OnChannelError() gets fired. + channel()->Close(); + + EXPECT_TRUE(WaitForClientShutdown()); + DestroyChannel(); + broker_.reset(); + } + + // Teardown shared between most tests. + void CommonTearDown() { + CheckChildResult(); + FinalCleanUp(); + } + + // Makes a SharedMemory region, fills it with |contents|, sends the handle + // over Chrome IPC, and unmaps the region. + void SendMessage1(const std::string& contents) { + scoped_ptr<base::SharedMemory> shared_memory(MakeSharedMemory(contents)); + IPC::Message* message = + new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200); + sender()->Send(message); + } + + ProxyListener* get_proxy_listener() { return &proxy_listener_; } + IPC::AttachmentBrokerUnprivilegedMac* get_broker() { return broker_.get(); } + AttachmentBrokerObserver* get_observer() { return &observer_; } + ResultListener* get_result_listener() { return &result_listener_; } + + protected: + // The number of active names immediately after set up. + mach_msg_type_number_t active_names_at_start_; + + private: + ProxyListener proxy_listener_; + scoped_ptr<IPC::AttachmentBrokerUnprivilegedMac> broker_; + AttachmentBrokerObserver observer_; + + // 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_; + + std::string service_name_; + + ResultListener result_listener_; +}; + +// These objects are globally accessible, and are expected to outlive all IPC +// Channels. +struct ChildProcessGlobals { + 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; + + // Total resident memory before running the message loop. + mach_vm_size_t initial_resident_size; + + // Whether to emit log statements while processing messages. + bool message_logging; +}; + +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() { + base::CommandLine cmd_line = *base::CommandLine::ForCurrentProcess(); + std::string service_name = + cmd_line.GetSwitchValueASCII(g_service_switch_name); + base::mac::ScopedMachSendRight server_port( + IPC::LookupServer(service_name.c_str())); + base::mac::ScopedMachReceiveRight client_port(IPC::MakeReceivingPort()); + + // Send the port that this process is listening on to the server. + IPC::SendMachPort( + server_port.get(), client_port.get(), MACH_MSG_TYPE_MAKE_SEND); + + // Receive the task port of the server process. + base::mac::ScopedMachSendRight server_task_port( + IPC::ReceiveMachPort(client_port.get())); + + scoped_ptr<ChildProcessGlobals> globals(new ChildProcessGlobals); + globals->broker.reset( + new IPC::AttachmentBrokerPrivilegedMac(&globals->port_provider)); + globals->port_provider.InsertEntry(getppid(), server_task_port.get()); + globals->server_task_port.reset(server_task_port.release()); + globals->message_logging = true; + return globals; +} + +int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback, + const char* channel_name) { + LOG(INFO) << "Privileged process start."; + scoped_ptr<ChildProcessGlobals> globals(CommonChildProcessSetUp()); + + mach_msg_type_number_t active_names_at_start = IPC::GetActiveNameCount(); + + base::MessageLoopForIO main_message_loop; + ProxyListener listener; + + scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( + IPCTestBase::GetChannelName(channel_name), &listener)); + globals->broker->RegisterCommunicationChannel(channel.get()); + CHECK(channel->Connect()); + + globals->initial_resident_size = GetResidentSize(); + + while (true) { + if (globals->message_logging) + LOG(INFO) << "Privileged process spinning run loop."; + base::MessageLoop::current()->Run(); + ProxyListener::Reason reason = listener.get_reason(); + if (reason == ProxyListener::CHANNEL_ERROR) + break; + + while (listener.has_message()) { + if (globals->message_logging) + LOG(INFO) << "Privileged process running callback."; + callback(channel.get(), listener.get_first_message(), globals.get()); + if (globals->message_logging) + LOG(INFO) << "Privileged process finishing callback."; + listener.pop_first_message(); + } + } + + if (active_names_at_start != IPC::GetActiveNameCount()) { + LOG(INFO) << "Memory leak!."; + } + LOG(INFO) << "Privileged process end."; + return 0; +} + +// An unprivileged process makes a shared memory region, and writes a string to +// it. The SharedMemoryHandle is sent to the privileged process using Chrome +// IPC. The privileged process checks that it received the same memory region. +TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandle) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("SendSharedMemoryHandle"); + + SendMessage1(kDataBuffer1); + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void SendSharedMemoryHandleCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + bool success = CheckContentsOfMessage1(message, kDataBuffer1); + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandle) { + return CommonPrivilegedProcessMain(&SendSharedMemoryHandleCallback, + "SendSharedMemoryHandle"); +} + +// Similar to SendSharedMemoryHandle, but sends a very long shared memory +// region. +TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleLong) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("SendSharedMemoryHandleLong"); + + std::string buffer(1 << 23, 'a'); + SendMessage1(buffer); + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void SendSharedMemoryHandleLongCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + std::string buffer(1 << 23, 'a'); + bool success = CheckContentsOfMessage1(message, buffer); + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleLong) { + return CommonPrivilegedProcessMain(&SendSharedMemoryHandleLongCallback, + "SendSharedMemoryHandleLong"); +} + +// Similar to SendSharedMemoryHandle, but sends two different shared memory +// regions in two messages. +TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesDifferentSharedMemoryHandle) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("SendTwoMessagesDifferentSharedMemoryHandle"); + + SendMessage1(kDataBuffer1); + SendMessage1(kDataBuffer2); + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void SendTwoMessagesDifferentSharedMemoryHandleCallback( + IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + static int count = 0; + static bool success = true; + ++count; + if (count == 1) { + success &= CheckContentsOfMessage1(message, kDataBuffer1); + } else if (count == 2) { + success &= CheckContentsOfMessage1(message, kDataBuffer2); + SendControlMessage(sender, success); + } +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendTwoMessagesDifferentSharedMemoryHandle) { + return CommonPrivilegedProcessMain( + &SendTwoMessagesDifferentSharedMemoryHandleCallback, + "SendTwoMessagesDifferentSharedMemoryHandle"); +} + +// Similar to SendSharedMemoryHandle, but sends the same shared memory region in +// two messages. +TEST_F(IPCAttachmentBrokerMacTest, SendTwoMessagesSameSharedMemoryHandle) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("SendTwoMessagesSameSharedMemoryHandle"); + + { + scoped_ptr<base::SharedMemory> shared_memory( + MakeSharedMemory(kDataBuffer1)); + + for (int i = 0; i < 2; ++i) { + IPC::Message* message = + new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200); + sender()->Send(message); + } + } + + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void SendTwoMessagesSameSharedMemoryHandleCallback( + IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + static int count = 0; + static base::SharedMemoryHandle handle1; + ++count; + + if (count == 1) { + handle1 = GetSharedMemoryHandleFromMsg1(message); + } else if (count == 2) { + base::SharedMemoryHandle handle2(GetSharedMemoryHandleFromMsg1(message)); + + bool success = CheckContentsOfTwoEquivalentSharedMemoryHandles( + handle1, handle2, kDataBuffer1); + SendControlMessage(sender, success); + } +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendTwoMessagesSameSharedMemoryHandle) { + return CommonPrivilegedProcessMain( + &SendTwoMessagesSameSharedMemoryHandleCallback, + "SendTwoMessagesSameSharedMemoryHandle"); +} + +// Similar to SendSharedMemoryHandle, but sends one message with two different +// memory regions. +TEST_F(IPCAttachmentBrokerMacTest, + SendOneMessageWithTwoDifferentSharedMemoryHandles) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("SendOneMessageWithTwoDifferentSharedMemoryHandles"); + + { + scoped_ptr<base::SharedMemory> shared_memory1( + MakeSharedMemory(kDataBuffer1)); + scoped_ptr<base::SharedMemory> shared_memory2( + MakeSharedMemory(kDataBuffer2)); + IPC::Message* message = new TestSharedMemoryHandleMsg2( + shared_memory1->handle(), shared_memory2->handle()); + sender()->Send(message); + } + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void SendOneMessageWithTwoDifferentSharedMemoryHandlesCallback( + IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + base::SharedMemoryHandle handle1; + base::SharedMemoryHandle handle2; + if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) { + LOG(ERROR) << "Failed to deserialize message."; + SendControlMessage(sender, false); + return; + } + + bool success = CheckContentsOfSharedMemoryHandle(handle1, kDataBuffer1) && + CheckContentsOfSharedMemoryHandle(handle2, kDataBuffer2); + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN( + SendOneMessageWithTwoDifferentSharedMemoryHandles) { + return CommonPrivilegedProcessMain( + &SendOneMessageWithTwoDifferentSharedMemoryHandlesCallback, + "SendOneMessageWithTwoDifferentSharedMemoryHandles"); +} + +// Similar to SendSharedMemoryHandle, but sends one message that contains the +// same memory region twice. +TEST_F(IPCAttachmentBrokerMacTest, + SendOneMessageWithTwoSameSharedMemoryHandles) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("SendOneMessageWithTwoSameSharedMemoryHandles"); + + { + scoped_ptr<base::SharedMemory> shared_memory( + MakeSharedMemory(kDataBuffer1)); + IPC::Message* message = new TestSharedMemoryHandleMsg2( + shared_memory->handle(), shared_memory->handle()); + sender()->Send(message); + } + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void SendOneMessageWithTwoSameSharedMemoryHandlesCallback( + IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + base::SharedMemoryHandle handle1; + base::SharedMemoryHandle handle2; + if (!GetSharedMemoryHandlesFromMsg2(message, &handle1, &handle2)) { + LOG(ERROR) << "Failed to deserialize message."; + SendControlMessage(sender, false); + return; + } + + bool success = CheckContentsOfTwoEquivalentSharedMemoryHandles( + handle1, handle2, kDataBuffer1); + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN( + SendOneMessageWithTwoSameSharedMemoryHandles) { + return CommonPrivilegedProcessMain( + &SendOneMessageWithTwoSameSharedMemoryHandlesCallback, + "SendOneMessageWithTwoSameSharedMemoryHandles"); +} + +// Sends one message with two Posix FDs and two Mach ports. +TEST_F(IPCAttachmentBrokerMacTest, SendPosixFDAndMachPort) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath fp1, fp2; + ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &fp1)); + ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &fp2)); + + CommonSetUp("SendPosixFDAndMachPort"); + + { + scoped_ptr<base::SharedMemory> shared_memory1( + MakeSharedMemory(kDataBuffer1)); + scoped_ptr<base::SharedMemory> shared_memory2( + MakeSharedMemory(kDataBuffer2)); + + base::FileDescriptor file_descriptor1( + MakeFileDescriptor(fp1, kDataBuffer3)); + base::FileDescriptor file_descriptor2( + MakeFileDescriptor(fp2, kDataBuffer4)); + + IPC::Message* message = new TestSharedMemoryHandleMsg3( + file_descriptor1, shared_memory1->handle(), file_descriptor2, + shared_memory2->handle()); + sender()->Send(message); + } + + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void SendPosixFDAndMachPortCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + TestSharedMemoryHandleMsg3::Schema::Param p; + if (!TestSharedMemoryHandleMsg3::Read(&message, &p)) { + LOG(ERROR) << "Failed to deserialize message."; + SendControlMessage(sender, false); + return; + } + + base::SharedMemoryHandle handle1 = base::get<1>(p); + base::SharedMemoryHandle handle2 = base::get<3>(p); + bool success1 = CheckContentsOfSharedMemoryHandle(handle1, kDataBuffer1) && + CheckContentsOfSharedMemoryHandle(handle2, kDataBuffer2); + if (!success1) + LOG(ERROR) << "SharedMemoryHandles have wrong contents."; + + bool success2 = + CheckContentsOfFileDescriptor(base::get<0>(p), kDataBuffer3) && + CheckContentsOfFileDescriptor(base::get<2>(p), kDataBuffer4); + if (!success2) + LOG(ERROR) << "FileDescriptors have wrong contents."; + + SendControlMessage(sender, success1 && success2); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendPosixFDAndMachPort) { + return CommonPrivilegedProcessMain(&SendPosixFDAndMachPortCallback, + "SendPosixFDAndMachPort"); +} + +// Similar to SendHandle, except the attachment's destination process is this +// process. This is an unrealistic scenario, but simulates an unprivileged +// process sending an attachment to another unprivileged process. +TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleToSelf) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + SetBroker(new MockBroker); + CommonSetUp("SendSharedMemoryHandleToSelf"); + + // 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); + + IPC::Message* message = + new TestSharedMemoryHandleMsg1(100, shared_memory->handle(), 200); + sender()->Send(message); + + // 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(); + + // Get the received attachment. + IPC::BrokerableAttachment::AttachmentId* id = get_observer()->get_id(); + ASSERT_TRUE(id); + scoped_refptr<IPC::BrokerableAttachment> received_attachment; + get_broker()->GetAttachmentWithId(*id, &received_attachment); + ASSERT_NE(received_attachment.get(), nullptr); + + // Check that it's has the same name, but that the ref count has increased. + base::mac::ScopedMachSendRight memory_object( + GetMachPortFromBrokeredAttachment(received_attachment)); + ASSERT_EQ(memory_object, shared_memory->handle().GetMemoryObject()); + EXPECT_EQ(ref_count + 1, + IPC::GetMachRefCount(shared_memory->handle().GetMemoryObject(), + MACH_PORT_RIGHT_SEND)); + } + + FinalCleanUp(); +} + +void SendSharedMemoryHandleToSelfCallback(IPC::Sender* sender, + const IPC::Message&, + ChildProcessGlobals* globals) { + // Do nothing special. The default behavior already runs the + // AttachmentBrokerPrivilegedMac. +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelf) { + return CommonPrivilegedProcessMain(&SendSharedMemoryHandleToSelfCallback, + "SendSharedMemoryHandleToSelf"); +} + +// Similar to SendSharedMemoryHandle, but uses a ChannelProxy instead of a +// Channel. +TEST_F(IPCAttachmentBrokerMacTest, SendSharedMemoryHandleChannelProxy) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + Init("SendSharedMemoryHandleChannelProxy"); + MachPreForkSetUp(); + + SetBroker(new IPC::AttachmentBrokerUnprivilegedMac); + get_broker()->AddObserver(get_observer(), task_runner()); + + scoped_ptr<base::Thread> thread( + new base::Thread("ChannelProxyTestServerThread")); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + thread->StartWithOptions(options); + + CreateChannelProxy(get_proxy_listener(), thread->task_runner().get()); + get_broker()->DesignateBrokerCommunicationChannel(channel_proxy()); + + ASSERT_TRUE(StartClient()); + + MachPostForkSetUp(); + active_names_at_start_ = IPC::GetActiveNameCount(); + get_proxy_listener()->set_listener(get_result_listener()); + + SendMessage1(kDataBuffer1); + base::MessageLoop::current()->Run(); + + CheckChildResult(); + + // There should be no leaked names. + EXPECT_EQ(active_names_at_start_, IPC::GetActiveNameCount()); + + // Close the channel so the client's OnChannelError() gets fired. + channel_proxy()->Close(); + + EXPECT_TRUE(WaitForClientShutdown()); + DestroyChannelProxy(); +} + +void SendSharedMemoryHandleChannelProxyCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + bool success = CheckContentsOfMessage1(message, kDataBuffer1); + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleChannelProxy) { + return CommonPrivilegedProcessMain( + &SendSharedMemoryHandleChannelProxyCallback, + "SendSharedMemoryHandleChannelProxy"); +} + +// Similar to SendSharedMemoryHandle, but first makes a copy of the handle using +// ShareToProcess(). +TEST_F(IPCAttachmentBrokerMacTest, ShareToProcess) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("ShareToProcess"); + + { + scoped_ptr<base::SharedMemory> shared_memory( + MakeSharedMemory(kDataBuffer1)); + base::SharedMemoryHandle new_handle; + ASSERT_TRUE(shared_memory->ShareToProcess(0, &new_handle)); + IPC::Message* message = + new TestSharedMemoryHandleMsg1(100, new_handle, 200); + sender()->Send(message); + } + + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void ShareToProcessCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + bool success = CheckContentsOfMessage1(message, kDataBuffer1); + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ShareToProcess) { + return CommonPrivilegedProcessMain(&ShareToProcessCallback, "ShareToProcess"); +} + +// Similar to ShareToProcess, but instead shares the memory object only with +// read permissions. +TEST_F(IPCAttachmentBrokerMacTest, ShareReadOnlyToProcess) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("ShareReadOnlyToProcess"); + + { + scoped_ptr<base::SharedMemory> shared_memory( + MakeSharedMemory(kDataBuffer1)); + base::SharedMemoryHandle new_handle; + ASSERT_TRUE(shared_memory->ShareReadOnlyToProcess(0, &new_handle)); + IPC::Message* message = + new TestSharedMemoryHandleMsg1(100, new_handle, 200); + sender()->Send(message); + } + + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void ShareReadOnlyToProcessCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message)); + + // Try to map the memory as writable. + scoped_ptr<base::SharedMemory> shared_memory( + MapSharedMemoryHandle(shm, false)); + ASSERT_EQ(nullptr, shared_memory->memory()); + + // Now try as read-only. + scoped_ptr<base::SharedMemory> shared_memory2( + MapSharedMemoryHandle(shm.Duplicate(), true)); + int current_prot, max_prot; + ASSERT_TRUE(IPC::GetMachProtections(shared_memory2->memory(), + shared_memory2->mapped_size(), + ¤t_prot, &max_prot)); + ASSERT_EQ(VM_PROT_READ, current_prot); + ASSERT_EQ(VM_PROT_READ, max_prot); + + bool success = + memcmp(shared_memory2->memory(), kDataBuffer1, strlen(kDataBuffer1)) == 0; + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(ShareReadOnlyToProcess) { + return CommonPrivilegedProcessMain(&ShareReadOnlyToProcessCallback, + "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"); +} + +// Tests the memory usage characteristics of attachment brokering a single large +// message. This test has the *potential* to be flaky, since it compares +// resident memory at different points in time, and that measurement is +// non-deterministic. +TEST_F(IPCAttachmentBrokerMacTest, MemoryUsageLargeMessage) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("MemoryUsageLargeMessage"); + + std::string test_string(g_large_message_size, 'a'); + SendMessage1(test_string); + base::MessageLoop::current()->Run(); + CommonTearDown(); +} + +void MemoryUsageLargeMessageCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + EXPECT_LE(GetResidentSize(), + globals->initial_resident_size + g_expected_memory_increase); + + base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message)); + scoped_ptr<base::SharedMemory> shared_memory( + MapSharedMemoryHandle(shm, false)); + EXPECT_LE(GetResidentSize(), + globals->initial_resident_size + g_expected_memory_increase); + + char* addr = static_cast<char*>(shared_memory->memory()); + for (size_t i = 0; i < g_large_message_size; i += 1024) { + addr[i] = 'a'; + } + EXPECT_GE(GetResidentSize(), + globals->initial_resident_size + g_large_message_size); + + shared_memory.reset(); +#if !defined(ADDRESS_SANITIZER) && !defined(LEAK_SANITIZER) && \ + !defined(MEMORY_SANITIZER) && !defined(THREAD_SANITIZER) && \ + !defined(UNDEFINED_SANITIZER) + // Under a sanitizer build, releasing memory does not necessarily reduce the + // amount of resident memory. + EXPECT_LE(GetResidentSize(), + globals->initial_resident_size + g_expected_memory_increase); +#endif + + SendControlMessage(sender, true); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(MemoryUsageLargeMessage) { + return CommonPrivilegedProcessMain(&MemoryUsageLargeMessageCallback, + "MemoryUsageLargeMessage"); +} + +// Tests the memory usage characteristics of attachment brokering many small +// messages. This test has the *potential* to be flaky, since it compares +// resident memory at different points in time, and that measurement is +// non-deterministic. +TEST_F(IPCAttachmentBrokerMacTest, MemoryUsageManyMessages) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + CommonSetUp("MemoryUsageManyMessages"); + + for (int i = 0; i < g_large_message_count; ++i) { + std::string message = base::IntToString(i); + message += '\0'; + size_t end = message.size(); + message.resize(g_medium_message_size); + std::fill(message.begin() + end, message.end(), 'a'); + SendMessage1(message); + + base::MessageLoop::current()->RunUntilIdle(); + } + + if (get_result_listener()->get_result() == RESULT_UNKNOWN) + base::MessageLoop::current()->Run(); + + CommonTearDown(); +} + +void MemoryUsageManyMessagesCallback(IPC::Sender* sender, + const IPC::Message& message, + ChildProcessGlobals* globals) { + static int message_index = 0; + + { + // Map the shared memory, and make sure that its pages are counting towards + // resident size. + base::SharedMemoryHandle shm(GetSharedMemoryHandleFromMsg1(message)); + scoped_ptr<base::SharedMemory> shared_memory( + MapSharedMemoryHandle(shm, false)); + + char* addr = static_cast<char*>(shared_memory->memory()); + std::string message_string(addr); + int message_int; + ASSERT_TRUE(base::StringToInt(message_string, &message_int)); + ASSERT_EQ(message_index, message_int); + for (size_t i = 0; i < g_medium_message_size; i += 1024) { + addr[i] = 'a'; + } + } + + ++message_index; + + if (message_index == 1) { + // Disable message logging, since it significantly contributes towards total + // memory usage. + LOG(INFO) << "Disable privileged process message logging."; + globals->message_logging = false; + } + + if (message_index == g_large_message_count) { + size_t memory_increase_kb = + (GetResidentSize() - globals->initial_resident_size) / 1024; + LOG(INFO) << "Increase in memory usage in KB: " << memory_increase_kb; + +#if defined(ADDRESS_SANITIZER) || defined(LEAK_SANITIZER) || \ + defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \ + defined(UNDEFINED_SANITIZER) + // Under a sanitizer build, releasing memory does not necessarily reduce the + // amount of resident memory. + bool success = true; +#else + // The total increase in resident size should be less than 1MB. The exact + // amount is not deterministic. + bool success = memory_increase_kb < 1024; +#endif + + SendControlMessage(sender, success); + } +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(MemoryUsageManyMessages) { + return CommonPrivilegedProcessMain(&MemoryUsageManyMessagesCallback, + "MemoryUsageManyMessages"); +} + +} // namespace diff --git a/chromium/ipc/attachment_broker_messages.h b/chromium/ipc/attachment_broker_messages.h index f0e103d007f..d27062ed6a2 100644 --- a/chromium/ipc/attachment_broker_messages.h +++ b/chromium/ipc/attachment_broker_messages.h @@ -6,6 +6,7 @@ // Multiply-included message file, hence no include guard. #include "base/process/process_handle.h" +#include "build/build_config.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_export.h" #include "ipc/ipc_message_macros.h" @@ -14,6 +15,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 +35,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 +60,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 +82,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/chromium/ipc/attachment_broker_privileged.cc b/chromium/ipc/attachment_broker_privileged.cc index 0f3ac48c7ed..85c1ac949cd 100644 --- a/chromium/ipc/attachment_broker_privileged.cc +++ b/chromium/ipc/attachment_broker_privileged.cc @@ -6,17 +6,114 @@ #include <algorithm> +#include "base/lazy_instance.h" #include "base/metrics/histogram_macros.h" +#include "build/build_config.h" #include "ipc/ipc_endpoint.h" +#if defined(OS_WIN) +#include "ipc/attachment_broker_privileged_win.h" +#endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include <mach/mach.h> + +#include "base/process/port_provider_mac.h" +#include "ipc/attachment_broker_privileged_mac.h" +#endif + namespace IPC { -AttachmentBrokerPrivileged::AttachmentBrokerPrivileged() {} +namespace { + +#if defined(OS_MACOSX) && !defined(OS_IOS) + +// A fake port provider that does nothing. Intended for single process unit +// tests. +class FakePortProvider : public base::PortProvider { + mach_port_t TaskForPid(base::ProcessHandle process) const override { + DCHECK_EQ(process, getpid()); + return mach_task_self(); + } +}; + +base::LazyInstance<FakePortProvider>::Leaky + g_fake_port_provider = LAZY_INSTANCE_INITIALIZER; + +// Passed as a constructor parameter to AttachmentBrokerPrivilegedMac. +base::PortProvider* g_port_provider = nullptr; +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + +// On platforms that support attachment brokering, returns a new instance of +// a platform-specific attachment broker. Otherwise returns |nullptr|. +// The caller takes ownership of the newly created instance, and is +// responsible for ensuring that the attachment broker lives longer than +// every IPC::Channel. The new instance automatically registers itself as the +// global attachment broker. +scoped_ptr<AttachmentBrokerPrivileged> CreateBroker() { +#if defined(OS_WIN) + return scoped_ptr<AttachmentBrokerPrivileged>( + new IPC::AttachmentBrokerPrivilegedWin); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + return scoped_ptr<AttachmentBrokerPrivileged>( + new IPC::AttachmentBrokerPrivilegedMac(g_port_provider)); +#else + return nullptr; +#endif +} + +// This class is wrapped in a LazyInstance to ensure that its constructor is +// only called once. The constructor creates an attachment broker and sets it as +// the global broker. +class AttachmentBrokerMakeOnce { + public: + AttachmentBrokerMakeOnce() { + attachment_broker_.reset(CreateBroker().release()); + } + + private: + scoped_ptr<IPC::AttachmentBrokerPrivileged> attachment_broker_; +}; -AttachmentBrokerPrivileged::~AttachmentBrokerPrivileged() {} +base::LazyInstance<AttachmentBrokerMakeOnce>::Leaky + g_attachment_broker_make_once = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +AttachmentBrokerPrivileged::AttachmentBrokerPrivileged() { + IPC::AttachmentBroker::SetGlobal(this); +} + +AttachmentBrokerPrivileged::~AttachmentBrokerPrivileged() { + IPC::AttachmentBroker::SetGlobal(nullptr); +} + +#if defined(OS_MACOSX) && !defined(OS_IOS) +// static +void AttachmentBrokerPrivileged::CreateBrokerIfNeeded( + base::PortProvider* provider) { + g_port_provider = provider; + g_attachment_broker_make_once.Get(); +} +#else +// static +void AttachmentBrokerPrivileged::CreateBrokerIfNeeded() { + g_attachment_broker_make_once.Get(); +} +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + +// static +void AttachmentBrokerPrivileged::CreateBrokerForSingleProcessTests() { +#if defined(OS_MACOSX) && !defined(OS_IOS) + CreateBrokerIfNeeded(&g_fake_port_provider.Get()); +#else + CreateBrokerIfNeeded(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) +} void AttachmentBrokerPrivileged::RegisterCommunicationChannel( Endpoint* endpoint) { + base::AutoLock auto_lock(*get_lock()); endpoint->SetAttachmentBrokerEndpoint(true); auto it = std::find(endpoints_.begin(), endpoints_.end(), endpoint); DCHECK(endpoints_.end() == it); @@ -25,12 +122,14 @@ void AttachmentBrokerPrivileged::RegisterCommunicationChannel( void AttachmentBrokerPrivileged::DeregisterCommunicationChannel( Endpoint* endpoint) { + base::AutoLock auto_lock(*get_lock()); auto it = std::find(endpoints_.begin(), endpoints_.end(), endpoint); if (it != endpoints_.end()) endpoints_.erase(it); } Sender* AttachmentBrokerPrivileged::GetSenderWithProcessId(base::ProcessId id) { + get_lock()->AssertAcquired(); auto it = std::find_if(endpoints_.begin(), endpoints_.end(), [id](Endpoint* c) { return c->GetPeerPID() == id; }); if (it == endpoints_.end()) diff --git a/chromium/ipc/attachment_broker_privileged.h b/chromium/ipc/attachment_broker_privileged.h index 7b3975acdb2..686bb9d282a 100644 --- a/chromium/ipc/attachment_broker_privileged.h +++ b/chromium/ipc/attachment_broker_privileged.h @@ -7,9 +7,18 @@ #include <vector> +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "build/build_config.h" #include "ipc/attachment_broker.h" #include "ipc/ipc_export.h" +#if defined(OS_MACOSX) && !defined(OS_IOS) +namespace base { +class PortProvider; +} // namespace base +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + namespace IPC { class Endpoint; @@ -24,16 +33,30 @@ class IPC_EXPORT AttachmentBrokerPrivileged : public IPC::AttachmentBroker { AttachmentBrokerPrivileged(); ~AttachmentBrokerPrivileged() override; - // Each unprivileged process should have one IPC channel on which it - // communicates attachment information with the broker process. In the broker - // process, these channels must be registered and deregistered with the - // Attachment Broker as they are created and destroyed. - void RegisterCommunicationChannel(Endpoint* endpoint); - void DeregisterCommunicationChannel(Endpoint* endpoint); + // If there is no global attachment broker, makes a new + // AttachmentBrokerPrivileged and sets it as the global attachment broker. + // This method is thread safe. +#if defined(OS_MACOSX) && !defined(OS_IOS) + static void CreateBrokerIfNeeded(base::PortProvider* provider); +#else + static void CreateBrokerIfNeeded(); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + + // Similar to CreateBrokerIfNeeded(), but useful for single process unit tests + // that don't need real attachment brokering, and don't want to deal with + // setting up a fake PortProvider. + static void CreateBrokerForSingleProcessTests(); + + // AttachmentBroker overrides. + void RegisterCommunicationChannel(Endpoint* endpoint) override; + void DeregisterCommunicationChannel(Endpoint* endpoint) override; protected: // Returns the sender whose peer's process id is |id|. // Returns nullptr if no sender is found. + // The lock returned by get_lock() must already be acquired before calling + // this method. The return value is only guaranteed to be valid while the lock + // is held. Sender* GetSenderWithProcessId(base::ProcessId id); // Errors that can be reported by subclasses. @@ -48,6 +71,32 @@ class IPC_EXPORT AttachmentBrokerPrivileged : public IPC::AttachmentBroker { DESTINATION_NOT_FOUND = 1, // The brokerable attachment did not have a destination process. NO_DESTINATION = 2, + // Error making an intermediate Mach port. + ERROR_MAKE_INTERMEDIATE = 3, + // Error parsing DuplicateMachPort message. + ERROR_PARSE_DUPLICATE_MACH_PORT_MESSAGE = 4, + // Couldn't get a task port for the process with a given pid. + ERROR_TASK_FOR_PID = 5, + // Couldn't make a port with receive rights in the destination process. + ERROR_MAKE_RECEIVE_PORT = 6, + // Couldn't change the attributes of a Mach port. + ERROR_SET_ATTRIBUTES = 7, + // 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, + // The broker did not have a channel of communication with the source + // process. + ERROR_SOURCE_NOT_FOUND = 12, + // The broker could not open the source or destination process with extra + // privileges. + ERROR_COULD_NOT_OPEN_SOURCE_OR_DEST = 13, + // The broker was asked to transfer a HANDLE with invalid permissions. + ERROR_INVALID_PERMISSIONS = 14, ERROR_MAX }; diff --git a/chromium/ipc/attachment_broker_privileged_mac.cc b/chromium/ipc/attachment_broker_privileged_mac.cc new file mode 100644 index 00000000000..c5bed27c68d --- /dev/null +++ b/chromium/ipc/attachment_broker_privileged_mac.cc @@ -0,0 +1,451 @@ +// 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 <stdint.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.h" +#include "base/synchronization/lock.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; + + kern_return_t kr = + 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 + + if (kr != KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), endpoint); + + return kr; +} + +} // namespace + +namespace IPC { + +AttachmentBrokerPrivilegedMac::AttachmentBrokerPrivilegedMac( + base::PortProvider* port_provider) + : port_provider_(port_provider) { + port_provider_->AddObserver(this); +} + +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, + base::ProcessId destination_process) { + switch (attachment->GetBrokerableType()) { + case BrokerableAttachment::MACH_PORT: { + internal::MachPortAttachmentMac* mach_port_attachment = + static_cast<internal::MachPortAttachmentMac*>(attachment.get()); + MachPortWireFormat wire_format = + mach_port_attachment->GetWireFormat(destination_process); + AddPrecursor(wire_format.destination_process, + base::mac::ScopedMachSendRight(wire_format.mach_port), + wire_format.attachment_id); + mach_port_attachment->reset_mach_port_ownership(); + SendPrecursorsForProcess(wire_format.destination_process); + return true; + } + default: + NOTREACHED(); + return false; + } + return false; +} + +void AttachmentBrokerPrivilegedMac::DeregisterCommunicationChannel( + Endpoint* endpoint) { + AttachmentBrokerPrivileged::DeregisterCommunicationChannel(endpoint); + + if (!endpoint) + return; + + base::ProcessId pid = endpoint->GetPeerPID(); + if (pid == base::kNullProcessId) + return; + + { + base::AutoLock l(precursors_lock_); + auto it = precursors_.find(pid); + if (it != precursors_.end()) { + delete it->second; + precursors_.erase(pid); + } + } + + { + base::AutoLock l(extractors_lock_); + auto it = extractors_.find(pid); + if (it != extractors_.end()) { + delete it->second; + extractors_.erase(pid); + } + } +} + +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::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); + return; + } + IPC::internal::MachPortAttachmentMac::WireFormat wire_format = + base::get<0>(param); + + if (wire_format.destination_process == base::kNullProcessId) { + LogError(NO_DESTINATION); + return; + } + + AddExtractor(message.get_sender_pid(), wire_format.destination_process, + wire_format.mach_port, wire_format.attachment_id); + ProcessExtractorsForProcess(message.get_sender_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); +} + +bool AttachmentBrokerPrivilegedMac::RouteWireFormatToAnother( + const MachPortWireFormat& wire_format) { + DCHECK_NE(wire_format.destination_process, base::Process::Current().Pid()); + + // Another process is the destination. + base::ProcessId dest = wire_format.destination_process; + base::AutoLock auto_lock(*get_lock()); + 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 false; + } + + LogError(DESTINATION_FOUND); + sender->Send(new AttachmentBrokerMsg_MachPortHasBeenDuplicated(wire_format)); + return true; +} + +mach_port_name_t AttachmentBrokerPrivilegedMac::CreateIntermediateMachPort( + mach_port_t task_port, + base::mac::ScopedMachSendRight port_to_insert) { + DCHECK_NE(mach_task_self(), task_port); + DCHECK_NE(static_cast<mach_port_name_t>(MACH_PORT_NULL), 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) { + LogError(ERROR_MAKE_RECEIVE_PORT); + 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) { + LogError(ERROR_SET_ATTRIBUTES); + 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) { + LogError(ERROR_EXTRACT_DEST_RIGHT); + 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.get(), MACH_MSG_TYPE_COPY_SEND); + if (kr != KERN_SUCCESS) { + LogError(ERROR_SEND_MACH_PORT); + 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::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_MOVE_SEND, + &extracted_right, &extracted_right_type); + 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); + + 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); +} + +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(); + + if (!to_self) { + base::AutoLock auto_lock(*get_lock()); + if (!GetSenderWithProcessId(pid)) { + // If there is no sender, then the destination process is no longer + // running, or never existed to begin with. + LogError(DESTINATION_NOT_FOUND); + delete it->second; + precursors_.erase(it); + return; + } + } + + mach_port_t task_port = port_provider_->TaskForPid(pid); + + // It's possible that the destination process has not yet provided the + // privileged process with its task port. + 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 { + if (!SendPrecursor(*precursor_it, task_port)) + break; + } + it->second->erase(precursor_it); + } + + delete it->second; + precursors_.erase(it); +} + +bool 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())); + } + return 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; + + { + base::AutoLock auto_lock(*get_lock()); + if (!GetSenderWithProcessId(pid)) { + // If there is no sender, then the source process is no longer running. + LogError(ERROR_SOURCE_NOT_FOUND); + delete it->second; + extractors_.erase(it); + return; + } + } + + mach_port_t task_port = port_provider_->TaskForPid(pid); + + // It's possible that the source process has not yet provided the privileged + // process with its task port. + 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/chromium/ipc/attachment_broker_privileged_mac.h b/chromium/ipc/attachment_broker_privileged_mac.h new file mode 100644 index 00000000000..c13cba01ead --- /dev/null +++ b/chromium/ipc/attachment_broker_privileged_mac.h @@ -0,0 +1,224 @@ +// 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 <stdint.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" + +namespace base { +class PortProvider; +} // namespace base + +namespace IPC { + +// This class is a concrete subclass of AttachmentBrokerPrivileged for the +// OSX platform. +// +// An example of the typical process by which a Mach port gets brokered. +// Definitions: +// 1. Let there be three processes P1, U2, U3. P1 is privileged. +// 2. U2 wants to send a Mach port M2 to U3. If this port is inserted into P1, +// it will be called M1. If it is inserted into U3, it will be called M3. +// 3. name() returns a serializable representation of a Mach port that can be +// passed over chrome IPC. +// 4. pid() returns the process id of a process. +// +// Process: +// 1. U2 sends a AttachmentBrokerMsg_DuplicateMachPort message to P1. The +// message contains name(M2), and pid(U3). +// 2. P1 extracts M2 into its own namespace, making M1. +// 3. P1 makes a new Mach port R in U3. +// 4. P1 sends a mach_msg with M1 to R. +// 5. P1 sends name(R) to U3. +// 6. U3 retrieves the queued message from R. The kernel automatically +// translates M1 into the namespace of U3, making M3. +// +// The logic of this class is a little bit more complex becauese any or all of +// P1, U2 and U3 may be the same, and depending on the exact configuration, +// the creation of R may not be necessary. +// +// 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 base::PortProvider::Observer { + public: + explicit AttachmentBrokerPrivilegedMac(base::PortProvider* port_provider); + ~AttachmentBrokerPrivilegedMac() override; + + // IPC::AttachmentBroker overrides. + bool SendAttachmentToProcess( + const scoped_refptr<IPC::BrokerableAttachment>& attachment, + base::ProcessId destination_process) override; + void DeregisterCommunicationChannel(Endpoint* endpoint) override; + + // 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); + FRIEND_TEST_ALL_PREFIXES(AttachmentBrokerPrivilegedMacMultiProcessTest, + InsertSameRightTwice); + 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); + + // 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); + + // |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. + // 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( + mach_port_t task_port, + base::mac::ScopedMachSendRight port_to_insert); + + // 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); + + // |wire_format.destination_process| must be this process. + // |wire_format.mach_port| must be the final Mach port. + // 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 RoutePrecursorToSelf(AttachmentPrecursor* precursor); + + // |wire_format.destination_process| must be another process. + // |wire_format.mach_port| must be the intermediate Mach port. + // Ownership of |wire_format.mach_port| is implicitly passed to the process + // that receives the Chrome IPC message. + // Returns |false| on irrecoverable error. + bool 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|. + // Returns |false| on irrecoverable error. + bool 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. + 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); +}; + +} // namespace IPC + +#endif // IPC_ATTACHMENT_BROKER_PRIVILEGED_MAC_H_ diff --git a/chromium/ipc/attachment_broker_privileged_mac_unittest.cc b/chromium/ipc/attachment_broker_privileged_mac_unittest.cc new file mode 100644 index 00000000000..15aecb66211 --- /dev/null +++ b/chromium/ipc/attachment_broker_privileged_mac_unittest.cc @@ -0,0 +1,435 @@ +// 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 <stddef.h> +#include <stdint.h> + +#include <map> + +#include "base/command_line.h" +#include "base/mac/mac_util.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/macros.h" +#include "base/memory/shared_memory.h" +#include "base/process/port_provider_mac.h" +#include "base/process/process_handle.h" +#include "base/sys_info.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#include "ipc/test_util_mac.h" +#include "testing/multiprocess_func_list.h" + +namespace IPC { + +namespace { + +static const std::string g_service_switch_name = "service_name"; + +// 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_id = 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; +} + +// 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.get(), client_port.get(), MACH_MSG_TYPE_MAKE_SEND); + + // Send the task port for this process. + SendMachPort(server_port.get(), 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; +} + +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 + : 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_.get()).release()); + client_task_port_.reset(ReceiveMachPort(server_port_.get()).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_; + + // Dummy port provider. + MockPortProvider port_provider_; + + 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) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + SetUpChild("InsertRightClient"); + mach_msg_type_number_t original_name_count = GetActiveNameCount(); + IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_); + + // Create some shared memory. + scoped_ptr<base::SharedMemory> shared_memory = + CreateAndPopulateSharedMemoryHandle(s_memory_size); + ASSERT_TRUE(shared_memory->handle().IsValid()); + + // Insert the memory object into the destination task, via an intermediate + // port. + IncrementMachRefCount(shared_memory->handle().GetMemoryObject(), + MACH_PORT_RIGHT_SEND); + mach_port_name_t inserted_memory_object = broker.CreateIntermediateMachPort( + client_task_port_.get(), base::mac::ScopedMachSendRight( + shared_memory->handle().GetMemoryObject())); + EXPECT_NE(inserted_memory_object, + static_cast<mach_port_name_t>(MACH_PORT_NULL)); + SendUInt32(client_port_.get(), 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.get())); + base::mac::ScopedMachSendRight memory_object( + ReceiveMachPort(inserted_port.get())); + 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) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + SetUpChild("InsertSameRightTwiceClient"); + mach_msg_type_number_t original_name_count = GetActiveNameCount(); + IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_); + + // Create some shared memory. + scoped_ptr<base::SharedMemory> shared_memory = + CreateAndPopulateSharedMemoryHandle(s_memory_size); + ASSERT_TRUE(shared_memory->handle().IsValid()); + + // Insert the memory object into the destination task, via an intermediate + // port, twice. + for (int i = 0; i < 2; ++i) { + IncrementMachRefCount(shared_memory->handle().GetMemoryObject(), + MACH_PORT_RIGHT_SEND); + mach_port_name_t inserted_memory_object = broker.CreateIntermediateMachPort( + client_task_port_.get(), + base::mac::ScopedMachSendRight( + shared_memory->handle().GetMemoryObject())); + EXPECT_NE(inserted_memory_object, + static_cast<mach_port_name_t>(MACH_PORT_NULL)); + SendUInt32(client_port_.get(), 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.get())); + base::mac::ScopedMachReceiveRight inserted_port2( + ReceiveUInt32(client_port.get())); + base::mac::ScopedMachSendRight memory_object( + ReceiveMachPort(inserted_port.get())); + base::mac::ScopedMachSendRight memory_object2( + ReceiveMachPort(inserted_port2.get())); + 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) { + // Mach-based SharedMemory isn't support on OSX 10.6. + if (base::mac::IsOSSnowLeopard()) + return; + + SetUpChild("InsertTwoRightsClient"); + mach_msg_type_number_t original_name_count = GetActiveNameCount(); + IPC::AttachmentBrokerPrivilegedMac broker(&port_provider_); + + 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 the memory object into the destination task, via an intermediate + // port. + IncrementMachRefCount(shared_memory->handle().GetMemoryObject(), + MACH_PORT_RIGHT_SEND); + mach_port_name_t inserted_memory_object = broker.CreateIntermediateMachPort( + client_task_port_.get(), + base::mac::ScopedMachSendRight( + shared_memory->handle().GetMemoryObject())); + EXPECT_NE(inserted_memory_object, + static_cast<mach_port_name_t>(MACH_PORT_NULL)); + SendUInt32(client_port_.get(), 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.get())); + base::mac::ScopedMachReceiveRight inserted_port2( + ReceiveUInt32(client_port.get())); + base::mac::ScopedMachSendRight memory_object( + ReceiveMachPort(inserted_port.get())); + base::mac::ScopedMachSendRight memory_object2( + ReceiveMachPort(inserted_port2.get())); + 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/chromium/ipc/attachment_broker_privileged_win.cc b/chromium/ipc/attachment_broker_privileged_win.cc index 6f3bc41649e..c749a09c980 100644 --- a/chromium/ipc/attachment_broker_privileged_win.cc +++ b/chromium/ipc/attachment_broker_privileged_win.cc @@ -19,16 +19,17 @@ AttachmentBrokerPrivilegedWin::AttachmentBrokerPrivilegedWin() {} AttachmentBrokerPrivilegedWin::~AttachmentBrokerPrivilegedWin() {} bool AttachmentBrokerPrivilegedWin::SendAttachmentToProcess( - const BrokerableAttachment* attachment, + const scoped_refptr<IPC::BrokerableAttachment>& attachment, base::ProcessId destination_process) { switch (attachment->GetBrokerableType()) { case BrokerableAttachment::WIN_HANDLE: { - const internal::HandleAttachmentWin* handle_attachment = - static_cast<const internal::HandleAttachmentWin*>(attachment); + internal::HandleAttachmentWin* handle_attachment = + static_cast<internal::HandleAttachmentWin*>(attachment.get()); HandleWireFormat wire_format = handle_attachment->GetWireFormat(destination_process); HandleWireFormat new_wire_format = DuplicateWinHandle(wire_format, base::Process::Current().Pid()); + handle_attachment->reset_handle_ownership(); if (new_wire_format.handle == 0) return false; RouteDuplicatedHandle(new_wire_format); @@ -82,6 +83,7 @@ void AttachmentBrokerPrivilegedWin::RouteDuplicatedHandle( // Another process is the destination. base::ProcessId dest = wire_format.destination_process; + base::AutoLock auto_lock(*get_lock()); Sender* sender = GetSenderWithProcessId(dest); if (!sender) { // Assuming that this message was not sent from a malicious process, the @@ -101,35 +103,45 @@ AttachmentBrokerPrivilegedWin::HandleWireFormat AttachmentBrokerPrivilegedWin::DuplicateWinHandle( const HandleWireFormat& wire_format, base::ProcessId source_pid) { + // If the source process is the destination process, then no additional work + // is required. + if (source_pid == wire_format.destination_process) + return wire_format; + + // If the handle is not valid, no additional work is required. + if (wire_format.handle == 0) + return wire_format; + base::Process source_process = base::Process::OpenWithExtraPrivileges(source_pid); base::Process dest_process = base::Process::OpenWithExtraPrivileges(wire_format.destination_process); - int new_wire_format_handle = 0; - if (source_process.Handle() && dest_process.Handle()) { - DWORD desired_access = 0; - DWORD options = 0; - switch (wire_format.permissions) { - case HandleWin::INVALID: - LOG(ERROR) << "Received invalid permissions for duplication."; - return CopyWireFormat(wire_format, 0); - case HandleWin::DUPLICATE: - options = DUPLICATE_SAME_ACCESS; - break; - case HandleWin::FILE_READ_WRITE: - desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; - break; - } - - HANDLE new_handle; - HANDLE original_handle = LongToHandle(wire_format.handle); - DWORD result = ::DuplicateHandle(source_process.Handle(), original_handle, - dest_process.Handle(), &new_handle, - desired_access, FALSE, options); + if (!source_process.Handle() || !dest_process.Handle()) { + LogError(ERROR_COULD_NOT_OPEN_SOURCE_OR_DEST); + return wire_format; + } - new_wire_format_handle = (result != 0) ? HandleToLong(new_handle) : 0; + DWORD desired_access = 0; + DWORD options = DUPLICATE_CLOSE_SOURCE; + switch (wire_format.permissions) { + case HandleWin::INVALID: + LogError(ERROR_INVALID_PERMISSIONS); + return CopyWireFormat(wire_format, 0); + case HandleWin::DUPLICATE: + options |= DUPLICATE_SAME_ACCESS; + break; + case HandleWin::FILE_READ_WRITE: + desired_access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; + break; } + HANDLE new_handle; + HANDLE original_handle = LongToHandle(wire_format.handle); + DWORD result = ::DuplicateHandle(source_process.Handle(), original_handle, + dest_process.Handle(), &new_handle, + desired_access, FALSE, options); + + int new_wire_format_handle = (result != 0) ? HandleToLong(new_handle) : 0; return CopyWireFormat(wire_format, new_wire_format_handle); } diff --git a/chromium/ipc/attachment_broker_privileged_win.h b/chromium/ipc/attachment_broker_privileged_win.h index e986a7b0679..467a97776ee 100644 --- a/chromium/ipc/attachment_broker_privileged_win.h +++ b/chromium/ipc/attachment_broker_privileged_win.h @@ -5,6 +5,7 @@ #ifndef IPC_ATTACHMENT_BROKER_PRIVILEGED_WIN_H_ #define IPC_ATTACHMENT_BROKER_PRIVILEGED_WIN_H_ +#include "base/macros.h" #include "ipc/attachment_broker_privileged.h" #include "ipc/handle_attachment_win.h" #include "ipc/ipc_export.h" @@ -20,8 +21,9 @@ class IPC_EXPORT AttachmentBrokerPrivilegedWin ~AttachmentBrokerPrivilegedWin() override; // IPC::AttachmentBroker overrides. - bool SendAttachmentToProcess(const BrokerableAttachment* attachment, - base::ProcessId destination_process) override; + bool SendAttachmentToProcess( + const scoped_refptr<IPC::BrokerableAttachment>& attachment, + base::ProcessId destination_process) override; // IPC::Listener overrides. bool OnMessageReceived(const Message& message) override; @@ -32,7 +34,7 @@ class IPC_EXPORT AttachmentBrokerPrivilegedWin void OnDuplicateWinHandle(const Message& message); // Duplicates |wire_Format| from |source_process| into its destination - // process. + // process. Closes the original HANDLE. HandleWireFormat DuplicateWinHandle(const HandleWireFormat& wire_format, base::ProcessId source_process); diff --git a/chromium/ipc/attachment_broker_privileged_win_unittest.cc b/chromium/ipc/attachment_broker_privileged_win_unittest.cc index 9c4a11952d4..b25214d9d95 100644 --- a/chromium/ipc/attachment_broker_privileged_win_unittest.cc +++ b/chromium/ipc/attachment_broker_privileged_win_unittest.cc @@ -10,6 +10,9 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/shared_memory.h" +#include "base/memory/shared_memory_handle.h" +#include "base/win/scoped_handle.h" #include "ipc/attachment_broker_privileged_win.h" #include "ipc/attachment_broker_unprivileged_win.h" #include "ipc/handle_attachment_win.h" @@ -21,7 +24,10 @@ namespace { +using base::win::ScopedHandle; + const char kDataBuffer[] = "This is some test data to write to the file."; +const size_t kSharedMemorySize = 20000; // Returns the contents of the file represented by |h| as a std::string. std::string ReadFromFile(HANDLE h) { @@ -33,80 +39,107 @@ std::string ReadFromFile(HANDLE h) { return success ? std::string(buffer, bytes_read) : std::string(); } -HANDLE GetHandleFromBrokeredAttachment( +ScopedHandle GetHandleFromBrokeredAttachment( const scoped_refptr<IPC::BrokerableAttachment>& attachment) { if (attachment->GetType() != IPC::BrokerableAttachment::TYPE_BROKERABLE_ATTACHMENT) { LOG(INFO) << "Attachment type not TYPE_BROKERABLE_ATTACHMENT."; - return nullptr; + return ScopedHandle(nullptr); } if (attachment->GetBrokerableType() != IPC::BrokerableAttachment::WIN_HANDLE) { LOG(INFO) << "Brokerable type not WIN_HANDLE."; - return nullptr; + return ScopedHandle(nullptr); } IPC::internal::HandleAttachmentWin* received_handle_attachment = static_cast<IPC::internal::HandleAttachmentWin*>(attachment.get()); - return received_handle_attachment->get_handle(); + ScopedHandle h(received_handle_attachment->get_handle()); + received_handle_attachment->reset_handle_ownership(); + return h; } // |message| must be deserializable as a TestHandleWinMsg. Returns the HANDLE, // or nullptr if deserialization failed. -HANDLE GetHandleFromTestHandleWinMsg(const IPC::Message& message) { +ScopedHandle GetHandleFromTestHandleWinMsg(const IPC::Message& message) { // Expect a message with a brokered attachment. if (!message.HasBrokerableAttachments()) { LOG(INFO) << "Message missing brokerable attachment."; - return nullptr; + return ScopedHandle(nullptr); } TestHandleWinMsg::Schema::Param p; bool success = TestHandleWinMsg::Read(&message, &p); if (!success) { LOG(INFO) << "Failed to deserialize message."; - return nullptr; + return ScopedHandle(nullptr); } IPC::HandleWin handle_win = base::get<1>(p); - return handle_win.get_handle(); + return ScopedHandle(handle_win.get_handle()); +} + +// Returns a mapped, shared memory region based on the handle in |message|. +scoped_ptr<base::SharedMemory> GetSharedMemoryFromSharedMemoryHandleMsg1( + const IPC::Message& message, + size_t size) { + // Expect a message with a brokered attachment. + if (!message.HasBrokerableAttachments()) { + LOG(INFO) << "Message missing brokerable attachment."; + return nullptr; + } + + TestSharedMemoryHandleMsg1::Schema::Param p; + bool success = TestSharedMemoryHandleMsg1::Read(&message, &p); + if (!success) { + LOG(INFO) << "Failed to deserialize message."; + return nullptr; + } + + base::SharedMemoryHandle handle = base::get<0>(p); + scoped_ptr<base::SharedMemory> shared_memory( + new base::SharedMemory(handle, false)); + + shared_memory->Map(size); + return shared_memory; } // |message| must be deserializable as a TestTwoHandleWinMsg. Returns the // HANDLE, or nullptr if deserialization failed. -HANDLE GetHandleFromTestTwoHandleWinMsg(const IPC::Message& message, - int index) { +bool GetHandleFromTestTwoHandleWinMsg(const IPC::Message& message, + HANDLE* h1, + HANDLE* h2) { // Expect a message with a brokered attachment. if (!message.HasBrokerableAttachments()) { LOG(INFO) << "Message missing brokerable attachment."; - return nullptr; + return false; } TestTwoHandleWinMsg::Schema::Param p; bool success = TestTwoHandleWinMsg::Read(&message, &p); if (!success) { LOG(INFO) << "Failed to deserialize message."; - return nullptr; + return false; } - IPC::HandleWin handle_win; - if (index == 0) - handle_win = base::get<0>(p); - else if (index == 1) - handle_win = base::get<1>(p); - return handle_win.get_handle(); + IPC::HandleWin handle_win = base::get<0>(p); + *h1 = handle_win.get_handle(); + handle_win = base::get<1>(p); + *h2 = handle_win.get_handle(); + return true; } // |message| must be deserializable as a TestHandleWinMsg. Returns true if the // attached file HANDLE has contents |kDataBuffer|. bool CheckContentsOfTestMessage(const IPC::Message& message) { - HANDLE h = GetHandleFromTestHandleWinMsg(message); - if (h == nullptr) { + ScopedHandle h(GetHandleFromTestHandleWinMsg(message)); + if (h.Get() == nullptr) { LOG(INFO) << "Failed to get handle from TestHandleWinMsg."; return false; } - std::string contents = ReadFromFile(h); + std::string contents = ReadFromFile(h.Get()); bool success = (contents == std::string(kDataBuffer)); if (!success) { LOG(INFO) << "Expected contents: " << std::string(kDataBuffer); @@ -115,6 +148,13 @@ bool CheckContentsOfTestMessage(const IPC::Message& message) { return success; } +// Returns 0 on error. +DWORD GetCurrentProcessHandleCount() { + DWORD handle_count = 0; + BOOL success = ::GetProcessHandleCount(::GetCurrentProcess(), &handle_count); + return success ? handle_count : 0; +} + enum TestResult { RESULT_UNKNOWN, RESULT_SUCCESS, @@ -220,21 +260,24 @@ class IPCAttachmentBrokerPrivilegedWinTest : public IPCTestBase { // Takes ownership of |broker|. Has no effect if called after CommonSetUp(). void set_broker(IPC::AttachmentBrokerUnprivilegedWin* broker) { broker_.reset(broker); - IPC::AttachmentBroker::SetGlobal(broker); } void CommonSetUp() { if (!broker_.get()) set_broker(new IPC::AttachmentBrokerUnprivilegedWin); - broker_->AddObserver(&observer_); - set_attachment_broker(broker_.get()); + broker_->AddObserver(&observer_, task_runner()); CreateChannel(&proxy_listener_); broker_->DesignateBrokerCommunicationChannel(channel()); ASSERT_TRUE(ConnectChannel()); ASSERT_TRUE(StartClient()); + + handle_count_ = GetCurrentProcessHandleCount(); + EXPECT_NE(handle_count_, 0u); } void CommonTearDown() { + EXPECT_EQ(handle_count_, handle_count_); + // Close the channel so the client's OnChannelError() gets fired. channel()->Close(); @@ -270,6 +313,7 @@ class IPCAttachmentBrokerPrivilegedWinTest : public IPCTestBase { ProxyListener proxy_listener_; scoped_ptr<IPC::AttachmentBrokerUnprivilegedWin> broker_; MockObserver observer_; + DWORD handle_count_; }; // A broker which always sets the current process as the destination process @@ -278,8 +322,9 @@ class MockBroker : public IPC::AttachmentBrokerUnprivilegedWin { public: MockBroker() {} ~MockBroker() override {} - bool SendAttachmentToProcess(const IPC::BrokerableAttachment* attachment, - base::ProcessId destination_process) override { + bool SendAttachmentToProcess( + const scoped_refptr<IPC::BrokerableAttachment>& attachment, + base::ProcessId destination_process) override { return IPC::AttachmentBrokerUnprivilegedWin::SendAttachmentToProcess( attachment, base::Process::Current().Pid()); } @@ -289,7 +334,7 @@ class MockBroker : public IPC::AttachmentBrokerUnprivilegedWin { // file HANDLE is sent to the privileged process using the attachment broker. // The privileged process dups the HANDLE into its own HANDLE table. This test // checks that the file has the same contents in the privileged process. -TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendHandle) { +TEST_F(IPCAttachmentBrokerPrivilegedWinTest, SendHandle) { Init("SendHandle"); CommonSetUp(); @@ -311,7 +356,7 @@ TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendHandle) { // Similar to SendHandle, except the file HANDLE attached to the message has // neither read nor write permissions. TEST_F(IPCAttachmentBrokerPrivilegedWinTest, - DISABLED_SendHandleWithoutPermissions) { + SendHandleWithoutPermissions) { Init("SendHandleWithoutPermissions"); CommonSetUp(); @@ -339,7 +384,7 @@ TEST_F(IPCAttachmentBrokerPrivilegedWinTest, // Similar to SendHandle, except the attachment's destination process is this // process. This is an unrealistic scenario, but simulates an unprivileged // process sending an attachment to another unprivileged process. -TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendHandleToSelf) { +TEST_F(IPCAttachmentBrokerPrivilegedWinTest, SendHandleToSelf) { Init("SendHandleToSelf"); set_broker(new MockBroker); @@ -359,13 +404,12 @@ TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendHandleToSelf) { get_broker()->GetAttachmentWithId(*id, &received_attachment); ASSERT_NE(received_attachment.get(), nullptr); - // Check that it's a new entry in the HANDLE table. - HANDLE h2 = GetHandleFromBrokeredAttachment(received_attachment); - EXPECT_NE(h2, h); - EXPECT_NE(h2, nullptr); + // Check that it's a different entry in the HANDLE table. + ScopedHandle h2(GetHandleFromBrokeredAttachment(received_attachment)); + EXPECT_NE(h2.Get(), h); - // But it still points to the same file. - std::string contents = ReadFromFile(h); + // But still points to the same file. + std::string contents = ReadFromFile(h2.Get()); EXPECT_EQ(contents, std::string(kDataBuffer)); CommonTearDown(); @@ -373,7 +417,7 @@ TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendHandleToSelf) { // Similar to SendHandle, but sends a message with two instances of the same // handle. -TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendTwoHandles) { +TEST_F(IPCAttachmentBrokerPrivilegedWinTest, SendTwoHandles) { Init("SendTwoHandles"); CommonSetUp(); @@ -381,8 +425,12 @@ TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendTwoHandles) { get_proxy_listener()->set_listener(&result_listener); HANDLE h = CreateTempFile(); + HANDLE h2; + BOOL result = ::DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), + &h2, 0, FALSE, DUPLICATE_SAME_ACCESS); + ASSERT_TRUE(result); IPC::HandleWin handle_win1(h, IPC::HandleWin::FILE_READ_WRITE); - IPC::HandleWin handle_win2(h, IPC::HandleWin::FILE_READ_WRITE); + IPC::HandleWin handle_win2(h2, IPC::HandleWin::FILE_READ_WRITE); IPC::Message* message = new TestTwoHandleWinMsg(handle_win1, handle_win2); sender()->Send(message); base::MessageLoop::current()->Run(); @@ -396,7 +444,7 @@ TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendTwoHandles) { } // Similar to SendHandle, but sends the same message twice. -TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendHandleTwice) { +TEST_F(IPCAttachmentBrokerPrivilegedWinTest, SendHandleTwice) { Init("SendHandleTwice"); CommonSetUp(); @@ -404,8 +452,35 @@ TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendHandleTwice) { get_proxy_listener()->set_listener(&result_listener); HANDLE h = CreateTempFile(); + HANDLE h2; + BOOL result = ::DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), + &h2, 0, FALSE, DUPLICATE_SAME_ACCESS); + ASSERT_TRUE(result); SendMessageWithAttachment(h); - SendMessageWithAttachment(h); + SendMessageWithAttachment(h2); + base::MessageLoop::current()->Run(); + + // Check the result. + ASSERT_EQ(ProxyListener::MESSAGE_RECEIVED, + get_proxy_listener()->get_reason()); + ASSERT_EQ(result_listener.get_result(), RESULT_SUCCESS); + + CommonTearDown(); +} + +// An unprivileged process makes a shared memory region and sends it to the +// privileged process. +TEST_F(IPCAttachmentBrokerPrivilegedWinTest, DISABLED_SendSharedMemoryHandle) { + Init("SendSharedMemoryHandle"); + + CommonSetUp(); + ResultListener result_listener; + get_proxy_listener()->set_listener(&result_listener); + + scoped_ptr<base::SharedMemory> shared_memory(new base::SharedMemory); + shared_memory->CreateAndMapAnonymous(kSharedMemorySize); + memcpy(shared_memory->memory(), kDataBuffer, strlen(kDataBuffer)); + sender()->Send(new TestSharedMemoryHandleMsg1(shared_memory->handle())); base::MessageLoop::current()->Run(); // Check the result. @@ -427,7 +502,6 @@ int CommonPrivilegedProcessMain(OnMessageReceivedCallback callback, // Set up IPC channel. IPC::AttachmentBrokerPrivilegedWin broker; - IPC::AttachmentBroker::SetGlobal(&broker); scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient( IPCTestBase::GetChannelName(channel_name), &listener)); broker.RegisterCommunicationChannel(channel.get()); @@ -463,14 +537,14 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandle) { void SendHandleWithoutPermissionsCallback(IPC::Sender* sender, const IPC::Message& message) { - HANDLE h = GetHandleFromTestHandleWinMsg(message); - if (h != nullptr) { - SetFilePointer(h, 0, nullptr, FILE_BEGIN); + ScopedHandle h(GetHandleFromTestHandleWinMsg(message)); + if (h.Get() != nullptr) { + SetFilePointer(h.Get(), 0, nullptr, FILE_BEGIN); char buffer[100]; DWORD bytes_read; BOOL success = - ::ReadFile(h, buffer, static_cast<DWORD>(strlen(kDataBuffer)), + ::ReadFile(h.Get(), buffer, static_cast<DWORD>(strlen(kDataBuffer)), &bytes_read, nullptr); if (!success && GetLastError() == ERROR_ACCESS_DENIED) { SendControlMessage(sender, true); @@ -498,8 +572,8 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandleToSelf) { void SendTwoHandlesCallback(IPC::Sender* sender, const IPC::Message& message) { // Check for two handles. - HANDLE h1 = GetHandleFromTestTwoHandleWinMsg(message, 0); - HANDLE h2 = GetHandleFromTestTwoHandleWinMsg(message, 1); + HANDLE h1, h2; + EXPECT_TRUE(GetHandleFromTestTwoHandleWinMsg(message, &h1, &h2)); if (h1 == nullptr || h2 == nullptr) { SendControlMessage(sender, false); return; @@ -561,4 +635,18 @@ MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendHandleTwice) { "SendHandleTwice"); } +void SendSharedMemoryHandleCallback(IPC::Sender* sender, + const IPC::Message& message) { + scoped_ptr<base::SharedMemory> shared_memory = + GetSharedMemoryFromSharedMemoryHandleMsg1(message, kSharedMemorySize); + bool success = + memcmp(shared_memory->memory(), kDataBuffer, strlen(kDataBuffer)) == 0; + SendControlMessage(sender, success); +} + +MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandle) { + return CommonPrivilegedProcessMain(&SendSharedMemoryHandleCallback, + "SendSharedMemoryHandle"); +} + } // namespace diff --git a/chromium/ipc/attachment_broker_unprivileged.cc b/chromium/ipc/attachment_broker_unprivileged.cc index ce4e8ab807a..9286a890057 100644 --- a/chromium/ipc/attachment_broker_unprivileged.cc +++ b/chromium/ipc/attachment_broker_unprivileged.cc @@ -5,15 +5,42 @@ #include "ipc/attachment_broker_unprivileged.h" #include "base/metrics/histogram_macros.h" +#include "build/build_config.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_endpoint.h" +#if defined(OS_WIN) +#include "ipc/attachment_broker_unprivileged_win.h" +#endif + +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include "ipc/attachment_broker_unprivileged_mac.h" +#endif + namespace IPC { AttachmentBrokerUnprivileged::AttachmentBrokerUnprivileged() - : sender_(nullptr) {} + : sender_(nullptr) { + IPC::AttachmentBroker::SetGlobal(this); +} -AttachmentBrokerUnprivileged::~AttachmentBrokerUnprivileged() {} +AttachmentBrokerUnprivileged::~AttachmentBrokerUnprivileged() { + IPC::AttachmentBroker::SetGlobal(nullptr); +} + +// static +scoped_ptr<AttachmentBrokerUnprivileged> +AttachmentBrokerUnprivileged::CreateBroker() { +#if defined(OS_WIN) + return scoped_ptr<AttachmentBrokerUnprivileged>( + new IPC::AttachmentBrokerUnprivilegedWin); +#elif defined(OS_MACOSX) && !defined(OS_IOS) + return scoped_ptr<AttachmentBrokerUnprivileged>( + new IPC::AttachmentBrokerUnprivilegedMac); +#else + return nullptr; +#endif +} void AttachmentBrokerUnprivileged::DesignateBrokerCommunicationChannel( Endpoint* endpoint) { diff --git a/chromium/ipc/attachment_broker_unprivileged.h b/chromium/ipc/attachment_broker_unprivileged.h index 4f847a62492..b572ff8266a 100644 --- a/chromium/ipc/attachment_broker_unprivileged.h +++ b/chromium/ipc/attachment_broker_unprivileged.h @@ -5,6 +5,8 @@ #ifndef IPC_ATTACHMENT_BROKER_UNPRIVILEGED_H_ #define IPC_ATTACHMENT_BROKER_UNPRIVILEGED_H_ +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "ipc/attachment_broker.h" #include "ipc/ipc_export.h" @@ -20,6 +22,14 @@ class IPC_EXPORT AttachmentBrokerUnprivileged : public IPC::AttachmentBroker { AttachmentBrokerUnprivileged(); ~AttachmentBrokerUnprivileged() override; + // On platforms that support attachment brokering, returns a new instance of + // a platform-specific attachment broker. Otherwise returns |nullptr|. + // The caller takes ownership of the newly created instance, and is + // responsible for ensuring that the attachment broker lives longer than + // every IPC::Channel. The new instance automatically registers itself as the + // global attachment broker. + static scoped_ptr<AttachmentBrokerUnprivileged> CreateBroker(); + // In each unprivileged process, exactly one channel should be used to // communicate brokerable attachments with the broker process. void DesignateBrokerCommunicationChannel(Endpoint* endpoint); @@ -28,7 +38,7 @@ class IPC_EXPORT AttachmentBrokerUnprivileged : public IPC::AttachmentBroker { IPC::Sender* get_sender() { return sender_; } // Errors that can be reported by subclasses. - // These match tools/metrics/histograms.xml. + // These match tools/metrics/histograms/histograms.xml. // This enum is append-only. enum UMAError { // The brokerable attachment was successfully processed. @@ -36,6 +46,8 @@ class IPC_EXPORT AttachmentBrokerUnprivileged : public IPC::AttachmentBroker { // The brokerable attachment's destination was not the process that received // the attachment. WRONG_DESTINATION = 1, + // An error occurred while trying to receive a Mach port with mach_msg(). + ERR_RECEIVE_MACH_MESSAGE = 2, ERROR_MAX }; diff --git a/chromium/ipc/attachment_broker_unprivileged_mac.cc b/chromium/ipc/attachment_broker_unprivileged_mac.cc new file mode 100644 index 00000000000..4e766887d9a --- /dev/null +++ b/chromium/ipc/attachment_broker_unprivileged_mac.cc @@ -0,0 +1,110 @@ +// 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 scoped_refptr<IPC::BrokerableAttachment>& attachment, + base::ProcessId destination_process) { + switch (attachment->GetBrokerableType()) { + case BrokerableAttachment::MACH_PORT: { + internal::MachPortAttachmentMac* mach_port_attachment = + static_cast<internal::MachPortAttachmentMac*>(attachment.get()); + internal::MachPortAttachmentMac::WireFormat format = + mach_port_attachment->GetWireFormat(destination_process); + bool success = + get_sender()->Send(new AttachmentBrokerMsg_DuplicateMachPort(format)); + if (success) + mach_port_attachment->reset_mach_port_ownership(); + return success; + } + 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()) { + // If everything is functioning correctly, this path should not be taken. + // However, it's still important to validate all fields of the IPC message. + LogError(WRONG_DESTINATION); + return; + } + + base::mac::ScopedMachReceiveRight message_port(wire_format.mach_port); + base::mac::ScopedMachSendRight memory_object( + ReceiveMachPort(message_port.get())); + + LogError(memory_object.get() == MACH_PORT_NULL ? ERR_RECEIVE_MACH_MESSAGE + : SUCCESS); + + 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/chromium/ipc/attachment_broker_unprivileged_mac.h b/chromium/ipc/attachment_broker_unprivileged_mac.h new file mode 100644 index 00000000000..aa058e3ae19 --- /dev/null +++ b/chromium/ipc/attachment_broker_unprivileged_mac.h @@ -0,0 +1,43 @@ +// 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 "base/macros.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 scoped_refptr<IPC::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/chromium/ipc/attachment_broker_unprivileged_win.cc b/chromium/ipc/attachment_broker_unprivileged_win.cc index 1151f53ac85..9e01d89fec1 100644 --- a/chromium/ipc/attachment_broker_unprivileged_win.cc +++ b/chromium/ipc/attachment_broker_unprivileged_win.cc @@ -17,16 +17,19 @@ AttachmentBrokerUnprivilegedWin::AttachmentBrokerUnprivilegedWin() {} AttachmentBrokerUnprivilegedWin::~AttachmentBrokerUnprivilegedWin() {} bool AttachmentBrokerUnprivilegedWin::SendAttachmentToProcess( - const BrokerableAttachment* attachment, + const scoped_refptr<BrokerableAttachment>& attachment, base::ProcessId destination_process) { switch (attachment->GetBrokerableType()) { case BrokerableAttachment::WIN_HANDLE: { - const internal::HandleAttachmentWin* handle_attachment = - static_cast<const internal::HandleAttachmentWin*>(attachment); + internal::HandleAttachmentWin* handle_attachment = + static_cast<internal::HandleAttachmentWin*>(attachment.get()); internal::HandleAttachmentWin::WireFormat format = handle_attachment->GetWireFormat(destination_process); - return get_sender()->Send( + bool success = get_sender()->Send( new AttachmentBrokerMsg_DuplicateWinHandle(format)); + if (success) + handle_attachment->reset_handle_ownership(); + return success; } case BrokerableAttachment::MACH_PORT: case BrokerableAttachment::PLACEHOLDER: diff --git a/chromium/ipc/attachment_broker_unprivileged_win.h b/chromium/ipc/attachment_broker_unprivileged_win.h index c3d29092741..2e37dc1ed19 100644 --- a/chromium/ipc/attachment_broker_unprivileged_win.h +++ b/chromium/ipc/attachment_broker_unprivileged_win.h @@ -5,6 +5,7 @@ #ifndef IPC_ATTACHMENT_BROKER_UNPRIVILEGED_WIN_H_ #define IPC_ATTACHMENT_BROKER_UNPRIVILEGED_WIN_H_ +#include "base/macros.h" #include "ipc/attachment_broker_unprivileged.h" #include "ipc/handle_attachment_win.h" #include "ipc/ipc_export.h" @@ -22,8 +23,9 @@ class IPC_EXPORT AttachmentBrokerUnprivilegedWin ~AttachmentBrokerUnprivilegedWin() override; // IPC::AttachmentBroker overrides. - bool SendAttachmentToProcess(const BrokerableAttachment* attachment, - base::ProcessId destination_process) override; + bool SendAttachmentToProcess( + const scoped_refptr<BrokerableAttachment>& attachment, + base::ProcessId destination_process) override; // IPC::Listener overrides. bool OnMessageReceived(const Message& message) override; diff --git a/chromium/ipc/attachment_broker_unprivileged_win_unittest.cc b/chromium/ipc/attachment_broker_unprivileged_win_unittest.cc deleted file mode 100644 index e67c54a61c7..00000000000 --- a/chromium/ipc/attachment_broker_unprivileged_win_unittest.cc +++ /dev/null @@ -1,51 +0,0 @@ -// 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 "build/build_config.h" - -#include <windows.h> - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/process/process.h" -#include "ipc/attachment_broker_messages.h" -#include "ipc/attachment_broker_unprivileged_win.h" -#include "ipc/handle_attachment_win.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace IPC { - -TEST(AttachmentBrokerUnprivilegedWinTest, ReceiveValidMessage) { - HANDLE handle = LongToHandle(8); - base::ProcessId destination = base::Process::Current().Pid(); - scoped_refptr<internal::HandleAttachmentWin> attachment( - new internal::HandleAttachmentWin(handle, HandleWin::DUPLICATE)); - AttachmentBrokerMsg_WinHandleHasBeenDuplicated msg( - attachment->GetWireFormat(destination)); - AttachmentBrokerUnprivilegedWin attachment_broker; - EXPECT_TRUE(attachment_broker.OnMessageReceived(msg)); - EXPECT_EQ(1u, attachment_broker.attachments_.size()); - - scoped_refptr<BrokerableAttachment> received_attachment = - attachment_broker.attachments_.front(); - EXPECT_EQ(BrokerableAttachment::WIN_HANDLE, - received_attachment->GetBrokerableType()); - internal::HandleAttachmentWin* received_handle_attachment = - static_cast<internal::HandleAttachmentWin*>(received_attachment.get()); - EXPECT_EQ(handle, received_handle_attachment->get_handle()); -} - -TEST(AttachmentBrokerUnprivilegedWinTest, ReceiveInvalidMessage) { - HANDLE handle = LongToHandle(8); - base::ProcessId destination = base::Process::Current().Pid() + 1; - scoped_refptr<internal::HandleAttachmentWin> attachment( - new internal::HandleAttachmentWin(handle, HandleWin::DUPLICATE)); - AttachmentBrokerMsg_WinHandleHasBeenDuplicated msg( - attachment->GetWireFormat(destination)); - AttachmentBrokerUnprivilegedWin attachment_broker; - EXPECT_TRUE(attachment_broker.OnMessageReceived(msg)); - EXPECT_EQ(0u, attachment_broker.attachments_.size()); -} - -} // namespace IPC diff --git a/chromium/ipc/brokerable_attachment.cc b/chromium/ipc/brokerable_attachment.cc index 50bf3338182..96ce5bb6193 100644 --- a/chromium/ipc/brokerable_attachment.cc +++ b/chromium/ipc/brokerable_attachment.cc @@ -4,6 +4,9 @@ #include "ipc/brokerable_attachment.h" +#include <stddef.h> + +#include "build/build_config.h" #include "ipc/attachment_broker.h" namespace IPC { diff --git a/chromium/ipc/brokerable_attachment.h b/chromium/ipc/brokerable_attachment.h index ce3082cd92b..50e7fd21eb9 100644 --- a/chromium/ipc/brokerable_attachment.h +++ b/chromium/ipc/brokerable_attachment.h @@ -5,9 +5,13 @@ #ifndef IPC_BROKERABLE_ATTACHMENT_H_ #define IPC_BROKERABLE_ATTACHMENT_H_ +#include <stddef.h> #include <stdint.h> +#include <algorithm> + #include "base/macros.h" +#include "build/build_config.h" #include "ipc/ipc_export.h" #include "ipc/ipc_message_attachment.h" @@ -37,21 +41,12 @@ class IPC_EXPORT BrokerableAttachment : public MessageAttachment { void SerializeToBuffer(char* start_address, size_t size); bool operator==(const AttachmentId& rhs) const { - for (size_t i = 0; i < kNonceSize; ++i) { - if (nonce[i] != rhs.nonce[i]) - return false; - } - return true; + return std::equal(nonce, nonce + kNonceSize, rhs.nonce); } bool operator<(const AttachmentId& rhs) const { - for (size_t i = 0; i < kNonceSize; ++i) { - if (nonce[i] < rhs.nonce[i]) - return true; - if (nonce[i] > rhs.nonce[i]) - return false; - } - return false; + return std::lexicographical_compare(nonce, nonce + kNonceSize, rhs.nonce, + rhs.nonce + kNonceSize); } }; diff --git a/chromium/ipc/brokerable_attachment_mac.cc b/chromium/ipc/brokerable_attachment_mac.cc new file mode 100644 index 00000000000..dbcd86b361e --- /dev/null +++ b/chromium/ipc/brokerable_attachment_mac.cc @@ -0,0 +1,22 @@ +// 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/brokerable_attachment.h" + +#include "crypto/random.h" + +namespace IPC { + +// static +BrokerableAttachment::AttachmentId +BrokerableAttachment::AttachmentId::CreateIdWithRandomNonce() { + AttachmentId id; + // In order to prevent mutually untrusted processes from stealing resources + // from one another, the nonce must be secret. This generates a 128-bit, + // cryptographicaly-strong random number. + crypto::RandBytes(id.nonce, BrokerableAttachment::kNonceSize); + return id; +} + +} // namespace IPC diff --git a/chromium/ipc/handle_attachment_win.cc b/chromium/ipc/handle_attachment_win.cc index c14adcb801c..39c4ef78c7f 100644 --- a/chromium/ipc/handle_attachment_win.cc +++ b/chromium/ipc/handle_attachment_win.cc @@ -11,20 +11,28 @@ namespace internal { HandleAttachmentWin::HandleAttachmentWin(const HANDLE& handle, HandleWin::Permissions permissions) - : handle_(handle), permissions_(permissions) {} + : handle_(INVALID_HANDLE_VALUE), + permissions_(HandleWin::INVALID), + owns_handle_(true) { + HANDLE duplicated_handle; + BOOL result = + ::DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), + &duplicated_handle, 0, FALSE, DUPLICATE_SAME_ACCESS); + if (result) { + handle_ = duplicated_handle; + permissions_ = permissions; + } +} HandleAttachmentWin::HandleAttachmentWin(const WireFormat& wire_format) : BrokerableAttachment(wire_format.attachment_id), handle_(LongToHandle(wire_format.handle)), - permissions_(wire_format.permissions) {} - -HandleAttachmentWin::HandleAttachmentWin( - const BrokerableAttachment::AttachmentId& id) - : BrokerableAttachment(id), - handle_(INVALID_HANDLE_VALUE), - permissions_(HandleWin::INVALID) {} + permissions_(wire_format.permissions), + owns_handle_(true) {} HandleAttachmentWin::~HandleAttachmentWin() { + if (handle_ != INVALID_HANDLE_VALUE && owns_handle_) + ::CloseHandle(handle_); } HandleAttachmentWin::BrokerableType HandleAttachmentWin::GetBrokerableType() diff --git a/chromium/ipc/handle_attachment_win.h b/chromium/ipc/handle_attachment_win.h index 08880850d4f..5e04bdb3626 100644 --- a/chromium/ipc/handle_attachment_win.h +++ b/chromium/ipc/handle_attachment_win.h @@ -41,7 +41,7 @@ class IPC_EXPORT HandleAttachmentWin : public BrokerableAttachment { // The type is int32_t instead of HANDLE because HANDLE gets typedefed to // void*, whose size varies between 32 and 64-bit processes. Using a // int32_t means that 64-bit processes will need to perform both up-casting - // and down-casting. This is performed using the appropriate Windows apis. + // and down-casting. This is performed using the appropriate Windows APIs. // A value of 0 is equivalent to an invalid handle. int32_t handle; @@ -54,9 +54,13 @@ class IPC_EXPORT HandleAttachmentWin : public BrokerableAttachment { AttachmentId attachment_id; }; + // This constructor makes a copy of |handle| and takes ownership of the + // result. Should only be called by the sender of a Chrome IPC message. HandleAttachmentWin(const HANDLE& handle, HandleWin::Permissions permissions); + + // This constructor takes ownership of |wire_format.handle| without making a + // copy. Should only be called by the receiver of a Chrome IPC message. explicit HandleAttachmentWin(const WireFormat& wire_format); - explicit HandleAttachmentWin(const BrokerableAttachment::AttachmentId& id); BrokerableType GetBrokerableType() const override; @@ -65,10 +69,23 @@ class IPC_EXPORT HandleAttachmentWin : public BrokerableAttachment { HANDLE get_handle() const { return handle_; } + // The caller of this method has taken ownership of |handle_|. + void reset_handle_ownership() { + owns_handle_ = false; + handle_ = INVALID_HANDLE_VALUE; + } + private: ~HandleAttachmentWin() override; HANDLE handle_; HandleWin::Permissions permissions_; + + // In the sender process, the attachment owns the HANDLE of a newly created + // message. The attachment broker will eventually take ownership, and set + // this member to |false|. + // In the destination process, the attachment owns the Mach port until a call + // to ParamTraits<HandleWin>::Read() takes ownership. + bool owns_handle_; }; } // namespace internal diff --git a/chromium/ipc/handle_win.cc b/chromium/ipc/handle_win.cc index 5d259059264..f964c832057 100644 --- a/chromium/ipc/handle_win.cc +++ b/chromium/ipc/handle_win.cc @@ -4,10 +4,14 @@ #include "ipc/handle_win.h" +#include <utility> + #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "ipc/handle_attachment_win.h" +#include "ipc/ipc_message.h" namespace IPC { @@ -21,7 +25,7 @@ void ParamTraits<HandleWin>::Write(Message* m, const param_type& p) { scoped_refptr<IPC::internal::HandleAttachmentWin> attachment( new IPC::internal::HandleAttachmentWin(p.get_handle(), p.get_permissions())); - if (!m->WriteAttachment(attachment.Pass())) + if (!m->WriteAttachment(std::move(attachment))) NOTREACHED(); } @@ -43,12 +47,13 @@ bool ParamTraits<HandleWin>::Read(const Message* m, IPC::internal::HandleAttachmentWin* handle_attachment = static_cast<IPC::internal::HandleAttachmentWin*>(brokerable_attachment); r->set_handle(handle_attachment->get_handle()); + handle_attachment->reset_handle_ownership(); return true; } // static void ParamTraits<HandleWin>::Log(const param_type& p, std::string* l) { - l->append(base::StringPrintf("0x%X", p.get_handle())); + l->append(base::StringPrintf("0x%p", p.get_handle())); l->append(base::IntToString(p.get_permissions())); } diff --git a/chromium/ipc/handle_win.h b/chromium/ipc/handle_win.h index a8f6978bb2d..38cddae10b1 100644 --- a/chromium/ipc/handle_win.h +++ b/chromium/ipc/handle_win.h @@ -7,14 +7,26 @@ #include <windows.h> +#include <string> + #include "ipc/ipc_export.h" -#include "ipc/ipc_message_macros.h" +#include "ipc/ipc_param_traits.h" + +namespace base { +class PickleIterator; +} // namespace base namespace IPC { +class Message; + // HandleWin is a wrapper around a Windows HANDLE that can be transported // across Chrome IPC channels that support attachment brokering. The HANDLE will // be duplicated into the destination process. +// +// The ownership semantics for the underlying |handle_| are complex. See +// ipc/mach_port_mac.h (the OSX analog of this class) for an extensive +// discussion. class IPC_EXPORT HandleWin { public: enum Permissions { diff --git a/chromium/ipc/ipc.gyp b/chromium/ipc/ipc.gyp index 7924ec626ac..05b99895112 100644 --- a/chromium/ipc/ipc.gyp +++ b/chromium/ipc/ipc.gyp @@ -29,7 +29,7 @@ ], }, 'conditions': [ - ['OS == "win"', { + ['OS == "win" or OS == "mac"', { 'dependencies': [ '../crypto/crypto.gyp:crypto', ], @@ -52,8 +52,9 @@ '..' ], 'sources': [ + 'attachment_broker_mac_unittest.cc', + 'attachment_broker_privileged_mac_unittest.cc', 'attachment_broker_privileged_win_unittest.cc', - 'attachment_broker_unprivileged_win_unittest.cc', 'ipc_channel_posix_unittest.cc', 'ipc_channel_proxy_unittest.cc', 'ipc_channel_reader_unittest.cc', @@ -154,6 +155,8 @@ 'ipc_test_channel_listener.h', 'ipc_test_sink.cc', 'ipc_test_sink.h', + 'test_util_mac.cc', + 'test_util_mac.h', ], }, ], @@ -211,9 +214,29 @@ 'test_suite_name': 'ipc_perftests', }, 'includes': [ '../build/apk_test.gypi' ], + } + ], + 'conditions': [ + ['test_isolation_mode != "noop"', { + 'targets': [ + { + 'target_name': 'ipc_tests_apk_run', + 'type': 'none', + 'dependencies': [ + 'ipc_tests_apk', + ], + 'includes': [ + '../build/isolate.gypi', + ], + 'sources': [ + 'ipc_tests_apk.isolate', + ], + }, + ], }], + ], }], - ['test_isolation_mode != "noop"', { + ['test_isolation_mode != "noop" and OS != "android"', { 'targets': [ { 'target_name': 'ipc_tests_run', diff --git a/chromium/ipc/ipc.gypi b/chromium/ipc/ipc.gypi index 696b34ad28e..dbd2099e4c3 100644 --- a/chromium/ipc/ipc.gypi +++ b/chromium/ipc/ipc.gypi @@ -16,14 +16,19 @@ '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', 'brokerable_attachment.h', + 'brokerable_attachment_mac.cc', 'brokerable_attachment_win.cc', 'handle_attachment_win.cc', 'handle_attachment_win.h', diff --git a/chromium/ipc/ipc_channel.cc b/chromium/ipc/ipc_channel.cc index 6772e1c5579..2d510478cc3 100644 --- a/chromium/ipc/ipc_channel.cc +++ b/chromium/ipc/ipc_channel.cc @@ -4,6 +4,7 @@ #include "ipc/ipc_channel.h" +#include <stddef.h> #include <stdint.h> #include <limits> @@ -11,6 +12,7 @@ #include "base/atomic_sequence_num.h" #include "base/rand_util.h" #include "base/strings/stringprintf.h" +#include "build/build_config.h" namespace { diff --git a/chromium/ipc/ipc_channel.h b/chromium/ipc/ipc_channel.h index ce3a00af19b..dd882bb5cc0 100644 --- a/chromium/ipc/ipc_channel.h +++ b/chromium/ipc/ipc_channel.h @@ -5,21 +5,23 @@ #ifndef IPC_IPC_CHANNEL_H_ #define IPC_IPC_CHANNEL_H_ +#include <stddef.h> #include <stdint.h> #include <string> -#if defined(OS_POSIX) -#include <sys/types.h> -#endif - #include "base/compiler_specific.h" #include "base/files/scoped_file.h" #include "base/process/process.h" +#include "build/build_config.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_endpoint.h" #include "ipc/ipc_message.h" +#if defined(OS_POSIX) +#include <sys/types.h> +#endif + namespace IPC { class Listener; @@ -73,7 +75,7 @@ class IPC_EXPORT Channel : public Endpoint { }; // Messages internal to the IPC implementation are defined here. - // Uses Maximum value of message type (uint16), to avoid conflicting + // Uses Maximum value of message type (uint16_t), to avoid conflicting // with normal message types, which are enumeration constants starting from 0. enum { // The Hello message is sent by the peer when the channel is connected. @@ -97,6 +99,11 @@ class IPC_EXPORT Channel : public Endpoint { // Amount of data to read at once from the pipe. static const size_t kReadBufferSize = 4 * 1024; + // Maximum persistent read buffer size. Read buffer can grow larger to + // accommodate large messages, but it's recommended to shrink back to this + // value because it fits 99.9% of all messages (see issue 529940 for data). + static const size_t kMaximumReadBufferSize = 64 * 1024; + // Initialize a Channel. // // |channel_handle| identifies the communication Channel. For POSIX, if @@ -239,10 +246,10 @@ class IPC_EXPORT Channel : public Endpoint { ~OutputElement(); size_t size() const { return message_ ? message_->size() : length_; } const void* data() const { return message_ ? message_->data() : buffer_; } - const Message* get_message() const { return message_.get(); } + Message* get_message() const { return message_.get(); } private: - scoped_ptr<const Message> message_; + scoped_ptr<Message> message_; void* buffer_; size_t length_; }; diff --git a/chromium/ipc/ipc_channel_common.cc b/chromium/ipc/ipc_channel_common.cc index f841dab8455..c12a360b64c 100644 --- a/chromium/ipc/ipc_channel_common.cc +++ b/chromium/ipc/ipc_channel_common.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "build/build_config.h" #include "ipc/ipc_channel.h" namespace IPC { diff --git a/chromium/ipc/ipc_channel_factory.cc b/chromium/ipc/ipc_channel_factory.cc index 78cd3633a54..6dda14d273c 100644 --- a/chromium/ipc/ipc_channel_factory.cc +++ b/chromium/ipc/ipc_channel_factory.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/macros.h" #include "ipc/ipc_channel_factory.h" namespace IPC { diff --git a/chromium/ipc/ipc_channel_nacl.cc b/chromium/ipc/ipc_channel_nacl.cc index 798657ab4e4..84d77f68d07 100644 --- a/chromium/ipc/ipc_channel_nacl.cc +++ b/chromium/ipc/ipc_channel_nacl.cc @@ -13,6 +13,7 @@ #include "base/bind.h" #include "base/logging.h" +#include "base/macros.h" #include "base/single_thread_task_runner.h" #include "base/synchronization/lock.h" #include "base/task_runner_util.h" @@ -286,7 +287,8 @@ bool ChannelNacl::ProcessOutgoingMessages() { output_queue_.pop_front(); int fds[MessageAttachmentSet::kMaxDescriptorsPerMessage]; - const size_t num_fds = msg->attachment_set()->size(); + const size_t num_fds = + msg->attachment_set()->num_non_brokerable_attachments(); DCHECK(num_fds <= MessageAttachmentSet::kMaxDescriptorsPerMessage); msg->attachment_set()->PeekDescriptors(fds); @@ -308,7 +310,7 @@ bool ChannelNacl::ProcessOutgoingMessages() { << msg->size(); return false; } else { - msg->attachment_set()->CommitAll(); + msg->attachment_set()->CommitAllDescriptors(); } // Message sent OK! diff --git a/chromium/ipc/ipc_channel_nacl.h b/chromium/ipc/ipc_channel_nacl.h index b7e97ccf4a7..0fcf85af829 100644 --- a/chromium/ipc/ipc_channel_nacl.h +++ b/chromium/ipc/ipc_channel_nacl.h @@ -8,6 +8,7 @@ #include <deque> #include <string> +#include "base/macros.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" diff --git a/chromium/ipc/ipc_channel_posix.cc b/chromium/ipc/ipc_channel_posix.cc index 3f5771bc1b6..f53a8fe36ec 100644 --- a/chromium/ipc/ipc_channel_posix.cc +++ b/chromium/ipc/ipc_channel_posix.cc @@ -21,8 +21,10 @@ #include <sys/un.h> #endif +#include <algorithm> #include <map> #include <string> +#include <utility> #include "base/command_line.h" #include "base/files/file_path.h" @@ -38,6 +40,7 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/synchronization/lock.h" +#include "build/build_config.h" #include "ipc/attachment_broker.h" #include "ipc/ipc_descriptors.h" #include "ipc/ipc_listener.h" @@ -378,27 +381,28 @@ void ChannelPosix::CloseFileDescriptors(Message* msg) { QueueCloseFDMessage(to_close[i], 2); } #else - msg->attachment_set()->CommitAll(); + msg->attachment_set()->CommitAllDescriptors(); #endif } bool ChannelPosix::ProcessOutgoingMessages() { - DCHECK(!waiting_connect_); // Why are we trying to send messages if there's - // no connection? + if (waiting_connect_) + return true; + if (is_blocked_on_write_) + return true; if (output_queue_.empty()) return true; - if (!pipe_.is_valid()) return false; // Write out all the messages we can till the write blocks or there are no // more outgoing messages. while (!output_queue_.empty()) { - Message* msg = output_queue_.front(); + OutputElement* element = output_queue_.front(); - size_t amt_to_write = msg->size() - message_send_bytes_written_; + size_t amt_to_write = element->size() - message_send_bytes_written_; DCHECK_NE(0U, amt_to_write); - const char* out_bytes = reinterpret_cast<const char*>(msg->data()) + + const char* out_bytes = reinterpret_cast<const char*>(element->data()) + message_send_bytes_written_; struct msghdr msgh = {0}; @@ -411,10 +415,13 @@ bool ChannelPosix::ProcessOutgoingMessages() { ssize_t bytes_written = 1; int fd_written = -1; - if (message_send_bytes_written_ == 0 && !msg->attachment_set()->empty()) { + Message* msg = element->get_message(); + if (message_send_bytes_written_ == 0 && msg && + msg->attachment_set()->num_non_brokerable_attachments()) { // This is the first chunk of a message which has descriptors to send struct cmsghdr *cmsg; - const unsigned num_fds = msg->attachment_set()->size(); + const unsigned num_fds = + msg->attachment_set()->num_non_brokerable_attachments(); DCHECK(num_fds <= MessageAttachmentSet::kMaxDescriptorsPerMessage); if (msg->attachment_set()->ContainsDirectoryDescriptor()) { @@ -446,7 +453,7 @@ bool ChannelPosix::ProcessOutgoingMessages() { fd_written = pipe_.get(); bytes_written = HANDLE_EINTR(sendmsg(pipe_.get(), &msgh, MSG_DONTWAIT)); } - if (bytes_written > 0) + if (bytes_written > 0 && msg) CloseFileDescriptors(msg); if (bytes_written < 0 && !SocketWriteErrorIsRecoverable()) { @@ -468,7 +475,7 @@ bool ChannelPosix::ProcessOutgoingMessages() { PLOG(ERROR) << "pipe error on " << fd_written << " Currently writing message of size: " - << msg->size(); + << element->size(); return false; } @@ -491,8 +498,13 @@ bool ChannelPosix::ProcessOutgoingMessages() { message_send_bytes_written_ = 0; // Message sent OK! - DVLOG(2) << "sent message @" << msg << " on channel @" << this - << " with type " << msg->type() << " on fd " << pipe_.get(); + if (msg) { + DVLOG(2) << "sent message @" << msg << " on channel @" << this + << " with type " << msg->type() << " on fd " << pipe_.get(); + } else { + DVLOG(2) << "sent buffer @" << element->data() << " on channel @" + << this << " on fd " << pipe_.get(); + } delete output_queue_.front(); output_queue_.pop(); } @@ -506,20 +518,18 @@ bool ChannelPosix::Send(Message* message) { << " with type " << message->type() << " (" << output_queue_.size() << " in queue)"; -#ifdef IPC_MESSAGE_LOG_ENABLED - Logging::GetInstance()->OnSendMessage(message, ""); -#endif // IPC_MESSAGE_LOG_ENABLED + if (!prelim_queue_.empty()) { + prelim_queue_.push(message); + return true; + } - TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"), - "ChannelPosix::Send", - message->flags(), - TRACE_EVENT_FLAG_FLOW_OUT); - output_queue_.push(message); - if (!is_blocked_on_write_ && !waiting_connect_) { - return ProcessOutgoingMessages(); + if (message->HasBrokerableAttachments() && + peer_pid_ == base::kNullProcessId) { + prelim_queue_.push(message); + return true; } - return true; + return ProcessMessageForDelivery(message); } AttachmentBroker* ChannelPosix::GetAttachmentBroker() { @@ -536,7 +546,7 @@ base::ScopedFD ChannelPosix::TakeClientFileDescriptor() { if (!client_pipe_.is_valid()) return base::ScopedFD(); PipeMap::GetInstance()->Remove(pipe_name_); - return client_pipe_.Pass(); + return std::move(client_pipe_); } void ChannelPosix::CloseClientFileDescriptor() { @@ -570,10 +580,11 @@ void ChannelPosix::ResetToAcceptingConnectionState() { ResetSafely(&pipe_); while (!output_queue_.empty()) { - Message* m = output_queue_.front(); + OutputElement* element = output_queue_.front(); output_queue_.pop(); - CloseFileDescriptors(m); - delete m; + if (element->get_message()) + CloseFileDescriptors(element->get_message()); + delete element; } // Close any outstanding, received file descriptors. @@ -668,10 +679,8 @@ void ChannelPosix::OnFileCanReadWithoutBlocking(int fd) { // only send our handshake message after we've processed the client's. // This gives us a chance to kill the client if the incoming handshake // is invalid. This also flushes any closefd messages. - if (!is_blocked_on_write_) { - if (!ProcessOutgoingMessages()) { - ClosePipeOnError(); - } + if (!ProcessOutgoingMessages()) { + ClosePipeOnError(); } } @@ -684,6 +693,72 @@ void ChannelPosix::OnFileCanWriteWithoutBlocking(int fd) { } } +bool ChannelPosix::ProcessMessageForDelivery(Message* message) { + // Sending a brokerable attachment requires a call to Channel::Send(), so + // Send() may be re-entrant. + if (message->HasBrokerableAttachments()) { + DCHECK(GetAttachmentBroker()); + DCHECK(peer_pid_ != base::kNullProcessId); + for (const scoped_refptr<IPC::BrokerableAttachment>& attachment : + message->attachment_set()->GetBrokerableAttachments()) { + if (!GetAttachmentBroker()->SendAttachmentToProcess(attachment, + peer_pid_)) { + delete message; + return false; + } + } + } + +#ifdef IPC_MESSAGE_LOG_ENABLED + Logging::GetInstance()->OnSendMessage(message, ""); +#endif // IPC_MESSAGE_LOG_ENABLED + + TRACE_EVENT_WITH_FLOW0(TRACE_DISABLED_BY_DEFAULT("ipc.flow"), + "ChannelPosix::Send", + message->flags(), + TRACE_EVENT_FLAG_FLOW_OUT); + + // |output_queue_| takes ownership of |message|. + OutputElement* element = new OutputElement(message); + output_queue_.push(element); + + if (message->HasBrokerableAttachments()) { + // |output_queue_| takes ownership of |ids.buffer|. + Message::SerializedAttachmentIds ids = + message->SerializedIdsOfBrokerableAttachments(); + output_queue_.push(new OutputElement(ids.buffer, ids.size)); + } + + return ProcessOutgoingMessages(); +} + +bool ChannelPosix::FlushPrelimQueue() { + DCHECK_NE(peer_pid_, base::kNullProcessId); + + // Due to the possibly re-entrant nature of ProcessMessageForDelivery(), + // |prelim_queue_| should appear empty. + std::queue<Message*> prelim_queue; + std::swap(prelim_queue_, prelim_queue); + + bool processing_error = false; + while (!prelim_queue.empty()) { + Message* m = prelim_queue.front(); + processing_error = !ProcessMessageForDelivery(m); + prelim_queue.pop(); + if (processing_error) + break; + } + + // Delete any unprocessed messages. + while (!prelim_queue.empty()) { + Message* m = prelim_queue.front(); + delete m; + prelim_queue.pop(); + } + + return !processing_error; +} + bool ChannelPosix::AcceptConnection() { base::MessageLoopForIO::current()->WatchFileDescriptor( pipe_.get(), @@ -747,7 +822,8 @@ void ChannelPosix::QueueHelloMessage() { if (!msg->WriteInt(GetHelloMessageProcId())) { NOTREACHED() << "Unable to pickle hello message proc id"; } - output_queue_.push(msg.release()); + OutputElement* element = new OutputElement(msg.release()); + output_queue_.push(element); } ChannelPosix::ReadState ChannelPosix::ReadData( @@ -902,8 +978,9 @@ void ChannelPosix::QueueCloseFDMessage(int fd, int hops) { if (!msg->WriteInt(hops - 1) || !msg->WriteInt(fd)) { NOTREACHED() << "Unable to pickle close fd."; } - // Send(msg.release()); - output_queue_.push(msg.release()); + + OutputElement* element = new OutputElement(msg.release()); + output_queue_.push(element); break; } @@ -929,6 +1006,9 @@ void ChannelPosix::HandleInternalMessage(const Message& msg) { peer_pid_ = pid; listener()->OnChannelConnected(pid); + + if (!FlushPrelimQueue()) + ClosePipeOnError(); break; #if defined(OS_MACOSX) diff --git a/chromium/ipc/ipc_channel_posix.h b/chromium/ipc/ipc_channel_posix.h index be47705db01..ddeb60ea365 100644 --- a/chromium/ipc/ipc_channel_posix.h +++ b/chromium/ipc/ipc_channel_posix.h @@ -7,6 +7,7 @@ #include "ipc/ipc_channel.h" +#include <stddef.h> #include <sys/socket.h> // for CMSG macros #include <queue> @@ -15,8 +16,10 @@ #include <vector> #include "base/files/scoped_file.h" +#include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/process/process.h" +#include "build/build_config.h" #include "ipc/ipc_channel_reader.h" #include "ipc/ipc_message_attachment_set.h" @@ -67,6 +70,13 @@ class IPC_EXPORT ChannelPosix : public Channel, private: bool CreatePipe(const IPC::ChannelHandle& channel_handle); + // Returns false on recoverable error. + // There are two reasons why this method might leave messages in the + // output_queue_. + // 1. |waiting_connect_| is |true|. + // 2. |is_blocked_on_write_| is |true|. + // If any of these conditionals change, this method should be called, as + // previously blocked messages may no longer be blocked. bool ProcessOutgoingMessages(); bool AcceptConnection(); @@ -100,6 +110,18 @@ class IPC_EXPORT ChannelPosix : public Channel, void OnFileCanReadWithoutBlocking(int fd) override; void OnFileCanWriteWithoutBlocking(int fd) override; + // Returns |false| on channel error. + // If |message| has brokerable attachments, those attachments are passed to + // the AttachmentBroker (which in turn invokes Send()), so this method must + // be re-entrant. + // Adds |message| to |output_queue_| and calls ProcessOutgoingMessages(). + bool ProcessMessageForDelivery(Message* message); + + // Moves all messages from |prelim_queue_| to |output_queue_| by calling + // ProcessMessageForDelivery(). + // Returns |false| on channel error. + bool FlushPrelimQueue(); + Mode mode_; base::ProcessId peer_pid_; @@ -135,8 +157,18 @@ class IPC_EXPORT ChannelPosix : public Channel, // the pipe. On POSIX it's used as a key in a local map of file descriptors. std::string pipe_name_; + // Messages not yet ready to be sent are queued here. Messages removed from + // this queue are placed in the output_queue_. The double queue is + // unfortunate, but is necessary because messages with brokerable attachments + // can generate multiple messages to be sent (possibly from other channels). + // Some of these generated messages cannot be sent until |peer_pid_| has been + // configured. + // As soon as |peer_pid| has been configured, there is no longer any need for + // |prelim_queue_|. All messages are flushed, and no new messages are added. + std::queue<Message*> prelim_queue_; + // Messages to be sent are queued here. - std::queue<Message*> output_queue_; + std::queue<OutputElement*> output_queue_; // We assume a worst case: kReadBufferSize bytes of messages, where each // message has no payload and a full complement of descriptors. diff --git a/chromium/ipc/ipc_channel_posix_unittest.cc b/chromium/ipc/ipc_channel_posix_unittest.cc index 99bd68b5e3f..c122b719508 100644 --- a/chromium/ipc/ipc_channel_posix_unittest.cc +++ b/chromium/ipc/ipc_channel_posix_unittest.cc @@ -6,8 +6,11 @@ #include "ipc/ipc_channel_posix.h" +#include <errno.h> #include <fcntl.h> +#include <stddef.h> #include <stdint.h> +#include <string.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> @@ -22,6 +25,7 @@ #include "base/single_thread_task_runner.h" #include "base/test/multiprocess_test.h" #include "base/test/test_timeouts.h" +#include "build/build_config.h" #include "ipc/ipc_listener.h" #include "ipc/unix_domain_socket_util.h" #include "testing/multiprocess_func_list.h" @@ -89,7 +93,7 @@ class IPCChannelPosixTestListener : public IPC::Listener { loop->QuitNow(); } else { // Die as soon as Run is called. - loop->task_runner()->PostTask(FROM_HERE, loop->QuitClosure()); + loop->task_runner()->PostTask(FROM_HERE, loop->QuitWhenIdleClosure()); } } @@ -189,7 +193,8 @@ void IPCChannelPosixTest::SpinRunLoop(base::TimeDelta delay) { // in the case of a bad test. Usually, the run loop will quit sooner than // that because all tests use a IPCChannelPosixTestListener which quits the // current run loop on any channel activity. - loop->task_runner()->PostDelayedTask(FROM_HERE, loop->QuitClosure(), delay); + loop->task_runner()->PostDelayedTask(FROM_HERE, loop->QuitWhenIdleClosure(), + delay); loop->Run(); } diff --git a/chromium/ipc/ipc_channel_proxy.cc b/chromium/ipc/ipc_channel_proxy.cc index 84f87fc8733..5e702617e0d 100644 --- a/chromium/ipc/ipc_channel_proxy.cc +++ b/chromium/ipc/ipc_channel_proxy.cc @@ -4,6 +4,10 @@ #include "ipc/ipc_channel_proxy.h" +#include <stddef.h> +#include <stdint.h> +#include <utility> + #include "base/bind.h" #include "base/compiler_specific.h" #include "base/location.h" @@ -12,6 +16,7 @@ #include "base/profiler/scoped_tracker.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" +#include "build/build_config.h" #include "ipc/ipc_channel_factory.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_logging.h" @@ -355,7 +360,7 @@ scoped_ptr<ChannelProxy> ChannelProxy::Create( const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner) { scoped_ptr<ChannelProxy> channel(new ChannelProxy(listener, ipc_task_runner)); channel->Init(channel_handle, mode, true); - return channel.Pass(); + return channel; } // static @@ -364,8 +369,8 @@ scoped_ptr<ChannelProxy> ChannelProxy::Create( Listener* listener, const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner) { scoped_ptr<ChannelProxy> channel(new ChannelProxy(listener, ipc_task_runner)); - channel->Init(factory.Pass(), true); - return channel.Pass(); + channel->Init(std::move(factory), true); + return channel; } ChannelProxy::ChannelProxy(Context* context) @@ -416,11 +421,11 @@ void ChannelProxy::Init(scoped_ptr<ChannelFactory> factory, // low-level pipe so that the client can connect. Without creating // the pipe immediately, it is possible for a listener to attempt // to connect and get an error since the pipe doesn't exist yet. - context_->CreateChannel(factory.Pass()); + context_->CreateChannel(std::move(factory)); } else { context_->ipc_task_runner()->PostTask( - FROM_HERE, base::Bind(&Context::CreateChannel, - context_.get(), Passed(factory.Pass()))); + FROM_HERE, base::Bind(&Context::CreateChannel, context_.get(), + base::Passed(&factory))); } // complete initialization on the background thread diff --git a/chromium/ipc/ipc_channel_proxy.h b/chromium/ipc/ipc_channel_proxy.h index f2eb82beba3..9fdffb1ca57 100644 --- a/chromium/ipc/ipc_channel_proxy.h +++ b/chromium/ipc/ipc_channel_proxy.h @@ -5,12 +5,15 @@ #ifndef IPC_IPC_CHANNEL_PROXY_H_ #define IPC_IPC_CHANNEL_PROXY_H_ +#include <stdint.h> + #include <vector> #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "base/threading/non_thread_safe.h" +#include "build/build_config.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_endpoint.h" @@ -215,6 +218,8 @@ class IPC_EXPORT ChannelProxy : public Endpoint, public base::NonThreadSafe { void set_attachment_broker_endpoint(bool is_endpoint) { attachment_broker_endpoint_ = is_endpoint; + if (channel_) + channel_->SetAttachmentBrokerEndpoint(is_endpoint); } // Methods called on the IO thread. diff --git a/chromium/ipc/ipc_channel_proxy_unittest.cc b/chromium/ipc/ipc_channel_proxy_unittest.cc index 7c5c8a70fca..f7f832b1445 100644 --- a/chromium/ipc/ipc_channel_proxy_unittest.cc +++ b/chromium/ipc/ipc_channel_proxy_unittest.cc @@ -4,6 +4,9 @@ #include "build/build_config.h" +#include <stddef.h> +#include <stdint.h> + #include "base/pickle.h" #include "base/threading/thread.h" #include "ipc/ipc_message.h" diff --git a/chromium/ipc/ipc_channel_reader.cc b/chromium/ipc/ipc_channel_reader.cc index 5ca0e46dd5a..9c6af49f2f1 100644 --- a/chromium/ipc/ipc_channel_reader.cc +++ b/chromium/ipc/ipc_channel_reader.cc @@ -4,8 +4,11 @@ #include "ipc/ipc_channel_reader.h" +#include <stddef.h> + #include <algorithm> +#include "base/message_loop/message_loop.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_logging.h" #include "ipc/ipc_message.h" @@ -15,7 +18,9 @@ namespace IPC { namespace internal { -ChannelReader::ChannelReader(Listener* listener) : listener_(listener) { +ChannelReader::ChannelReader(Listener* listener) + : listener_(listener), + max_input_buffer_size_(Channel::kMaximumReadBufferSize) { memset(input_buf_, 0, sizeof(input_buf_)); } @@ -68,6 +73,12 @@ void ChannelReader::CleanUp() { } } +void ChannelReader::DispatchMessage(Message* m) { + EmitLogBeforeDispatch(*m); + listener_->OnMessageReceived(*m); + HandleDispatchError(*m); +} + bool ChannelReader::TranslateInputData(const char* input_data, int input_data_len) { const char* p; @@ -95,33 +106,9 @@ bool ChannelReader::TranslateInputData(const char* input_data, int pickle_len = static_cast<int>(info.pickle_end - p); Message translated_message(p, pickle_len); - for (const auto& id : info.attachment_ids) - translated_message.AddPlaceholderBrokerableAttachmentWithId(id); - - if (!GetNonBrokeredAttachments(&translated_message)) + if (!HandleTranslatedMessage(&translated_message, info.attachment_ids)) return false; - // If there are no queued messages, attempt to immediately dispatch the - // newly translated message. - if (queued_messages_.empty()) { - DCHECK(blocked_ids_.empty()); - AttachmentIdSet blocked_ids = - GetBrokeredAttachments(&translated_message); - - if (blocked_ids.empty()) { - // Dispatch the message and continue the loop. - DispatchMessage(&translated_message); - p = info.message_end; - continue; - } - - blocked_ids_.swap(blocked_ids); - StartObservingAttachmentBroker(); - } - - // Make a deep copy of |translated_message| to add to the queue. - scoped_ptr<Message> m(new Message(translated_message)); - queued_messages_.push_back(m.release()); p = info.message_end; } else { // Last message is partial. @@ -132,25 +119,135 @@ bool ChannelReader::TranslateInputData(const char* input_data, } } + // Account for the case where last message's byte is in the next data chunk. + size_t next_message_buffer_size = next_message_size ? + next_message_size + Channel::kReadBufferSize - 1: + 0; + // Save any partial data in the overflow buffer. - input_overflow_buf_.assign(p, end - p); + if (p != input_overflow_buf_.data()) + input_overflow_buf_.assign(p, end - p); if (!input_overflow_buf_.empty()) { // We have something in the overflow buffer, which means that we will // append the next data chunk (instead of parsing it directly). So we // resize the buffer to fit the next message, to avoid repeatedly // growing the buffer as we receive all message' data chunks. - next_message_size += Channel::kReadBufferSize - 1; - if (next_message_size > input_overflow_buf_.capacity()) { - input_overflow_buf_.reserve(next_message_size); + if (next_message_buffer_size > input_overflow_buf_.capacity()) { + input_overflow_buf_.reserve(next_message_buffer_size); } } + // Trim the buffer if we can + if (next_message_buffer_size < max_input_buffer_size_ && + input_overflow_buf_.size() < max_input_buffer_size_ && + input_overflow_buf_.capacity() > max_input_buffer_size_) { + // std::string doesn't really have a method to shrink capacity to + // a specific value, so we have to swap with another string. + std::string trimmed_buf; + trimmed_buf.reserve(max_input_buffer_size_); + if (trimmed_buf.capacity() > max_input_buffer_size_) { + // Since we don't control how much space reserve() actually reserves, + // we have to go other way around and change the max size to avoid + // getting into the outer if() again. + max_input_buffer_size_ = trimmed_buf.capacity(); + } + trimmed_buf.assign(input_overflow_buf_.data(), + input_overflow_buf_.size()); + input_overflow_buf_.swap(trimmed_buf); + } + if (input_overflow_buf_.empty() && !DidEmptyInputBuffers()) return false; return true; } +bool ChannelReader::HandleTranslatedMessage( + Message* translated_message, + const AttachmentIdVector& attachment_ids) { + // Immediately handle internal messages. + if (IsInternalMessage(*translated_message)) { + EmitLogBeforeDispatch(*translated_message); + HandleInternalMessage(*translated_message); + HandleDispatchError(*translated_message); + return true; + } + + translated_message->set_sender_pid(GetSenderPID()); + + // Immediately handle attachment broker messages. + if (DispatchAttachmentBrokerMessage(*translated_message)) { + // Ideally, the log would have been emitted prior to dispatching the + // message, but that would require this class to know more about the + // internals of attachment brokering, which should be avoided. + EmitLogBeforeDispatch(*translated_message); + HandleDispatchError(*translated_message); + return true; + } + + return HandleExternalMessage(translated_message, attachment_ids); +} + +bool ChannelReader::HandleExternalMessage( + Message* external_message, + const AttachmentIdVector& attachment_ids) { + for (const auto& id : attachment_ids) + external_message->AddPlaceholderBrokerableAttachmentWithId(id); + + if (!GetNonBrokeredAttachments(external_message)) + return false; + + // If there are no queued messages, attempt to immediately dispatch the + // newly translated message. + if (queued_messages_.empty()) { + DCHECK(blocked_ids_.empty()); + AttachmentIdSet blocked_ids = GetBrokeredAttachments(external_message); + + if (blocked_ids.empty()) { + DispatchMessage(external_message); + return true; + } + + blocked_ids_.swap(blocked_ids); + StartObservingAttachmentBroker(); + } + + // Make a deep copy of |external_message| to add to the queue. + scoped_ptr<Message> m(new Message(*external_message)); + queued_messages_.push_back(m.release()); + return true; +} + +void ChannelReader::HandleDispatchError(const Message& message) { + if (message.dispatch_error()) + listener_->OnBadMessageReceived(message); +} + +void ChannelReader::EmitLogBeforeDispatch(const Message& message) { +#ifdef IPC_MESSAGE_LOG_ENABLED + std::string name; + Logging::GetInstance()->GetMessageText(message.type(), &name, &message, NULL); + TRACE_EVENT_WITH_FLOW1("ipc,toplevel", "ChannelReader::DispatchInputData", + message.flags(), TRACE_EVENT_FLAG_FLOW_IN, "name", + name); +#else + TRACE_EVENT_WITH_FLOW2("ipc,toplevel", "ChannelReader::DispatchInputData", + message.flags(), TRACE_EVENT_FLAG_FLOW_IN, "class", + IPC_MESSAGE_ID_CLASS(message.type()), "line", + IPC_MESSAGE_ID_LINE(message.type())); +#endif +} + +bool ChannelReader::DispatchAttachmentBrokerMessage(const Message& message) { +#if USE_ATTACHMENT_BROKER + if (IsAttachmentBrokerEndpoint() && GetAttachmentBroker()) { + return GetAttachmentBroker()->OnMessageReceived(message); + } +#endif // USE_ATTACHMENT_BROKER + + return false; +} + ChannelReader::DispatchState ChannelReader::DispatchMessages() { while (!queued_messages_.empty()) { if (!blocked_ids_.empty()) @@ -171,53 +268,18 @@ ChannelReader::DispatchState ChannelReader::DispatchMessages() { return DISPATCH_FINISHED; } -void ChannelReader::DispatchMessage(Message* m) { - m->set_sender_pid(GetSenderPID()); - -#ifdef IPC_MESSAGE_LOG_ENABLED - std::string name; - Logging::GetInstance()->GetMessageText(m->type(), &name, m, NULL); - TRACE_EVENT_WITH_FLOW1("ipc,toplevel", - "ChannelReader::DispatchInputData", - m->flags(), - TRACE_EVENT_FLAG_FLOW_IN, - "name", name); -#else - TRACE_EVENT_WITH_FLOW2("ipc,toplevel", - "ChannelReader::DispatchInputData", - m->flags(), - TRACE_EVENT_FLAG_FLOW_IN, - "class", IPC_MESSAGE_ID_CLASS(m->type()), - "line", IPC_MESSAGE_ID_LINE(m->type())); -#endif - - bool handled = false; - if (IsInternalMessage(*m)) { - HandleInternalMessage(*m); - handled = true; - } -#if USE_ATTACHMENT_BROKER - if (!handled && IsAttachmentBrokerEndpoint() && GetAttachmentBroker()) { - handled = GetAttachmentBroker()->OnMessageReceived(*m); - } -#endif // USE_ATTACHMENT_BROKER - if (!handled) - listener_->OnMessageReceived(*m); - if (m->dispatch_error()) - listener_->OnBadMessageReceived(*m); -} - ChannelReader::AttachmentIdSet ChannelReader::GetBrokeredAttachments( Message* msg) { std::set<BrokerableAttachment::AttachmentId> blocked_ids; #if USE_ATTACHMENT_BROKER MessageAttachmentSet* set = msg->attachment_set(); - std::vector<const BrokerableAttachment*> brokerable_attachments_copy = - set->PeekBrokerableAttachments(); - for (const BrokerableAttachment* attachment : brokerable_attachments_copy) { + std::vector<scoped_refptr<IPC::BrokerableAttachment>> + brokerable_attachments_copy(set->GetBrokerableAttachments()); + for (const auto& attachment : brokerable_attachments_copy) { if (attachment->NeedsBrokering()) { AttachmentBroker* broker = GetAttachmentBroker(); + DCHECK(broker); scoped_refptr<BrokerableAttachment> brokered_attachment; bool result = broker->GetAttachmentWithId(attachment->GetIdentifier(), &brokered_attachment); @@ -251,7 +313,8 @@ void ChannelReader::ReceivedBrokerableAttachmentWithId( void ChannelReader::StartObservingAttachmentBroker() { #if USE_ATTACHMENT_BROKER - GetAttachmentBroker()->AddObserver(this); + GetAttachmentBroker()->AddObserver( + this, base::MessageLoopForIO::current()->task_runner()); #endif // USE_ATTACHMENT_BROKER } diff --git a/chromium/ipc/ipc_channel_reader.h b/chromium/ipc/ipc_channel_reader.h index 46775226f9a..ca2bd9476e5 100644 --- a/chromium/ipc/ipc_channel_reader.h +++ b/chromium/ipc/ipc_channel_reader.h @@ -5,6 +5,8 @@ #ifndef IPC_IPC_CHANNEL_READER_H_ #define IPC_IPC_CHANNEL_READER_H_ +#include <stddef.h> + #include <set> #include "base/gtest_prod_util.h" @@ -129,17 +131,42 @@ class IPC_EXPORT ChannelReader : public SupportsAttachmentBrokering, FRIEND_TEST_ALL_PREFIXES(ChannelReaderTest, AttachmentNotYetBrokered); FRIEND_TEST_ALL_PREFIXES(ChannelReaderTest, ResizeOverflowBuffer); FRIEND_TEST_ALL_PREFIXES(ChannelReaderTest, InvalidMessageSize); + FRIEND_TEST_ALL_PREFIXES(ChannelReaderTest, TrimBuffer); - typedef std::set<BrokerableAttachment::AttachmentId> AttachmentIdSet; + using AttachmentIdSet = std::set<BrokerableAttachment::AttachmentId>; + using AttachmentIdVector = std::vector<BrokerableAttachment::AttachmentId>; - // Takes the given data received from the IPC channel, translates it into - // Messages, and puts them in queued_messages_. - // As an optimization, after a message is translated, the message is - // immediately dispatched if able. This prevents an otherwise unnecessary deep - // copy of the message which is needed to store the message in the message - // queue. + // Takes the data received from the IPC channel and translates it into + // Messages. Complete messages are passed to HandleTranslatedMessage(). + // Returns |false| on unrecoverable error. bool TranslateInputData(const char* input_data, int input_data_len); + // Internal messages and messages bound for the attachment broker are + // immediately dispatched. Other messages are passed to + // HandleExternalMessage(). + // Returns |false| on unrecoverable error. + bool HandleTranslatedMessage(Message* translated_message, + const AttachmentIdVector& attachment_ids); + + // Populates the message with brokered and non-brokered attachments. If + // possible, the message is immediately dispatched. Otherwise, a deep copy of + // the message is added to |queued_messages_|. |blocked_ids_| are updated if + // necessary. + bool HandleExternalMessage(Message* external_message, + const AttachmentIdVector& attachment_ids); + + // If there was a dispatch error, informs |listener_|. + void HandleDispatchError(const Message& message); + + // Emits logging associated with a Message that is about to be dispatched. + void EmitLogBeforeDispatch(const Message& message); + + // Attachment broker messages should be dispatched out of band, since there + // are no ordering restrictions on them, and they may be required to dispatch + // the messages waiting in |queued_messages_|. + // Returns true if the attachment broker handled |message|. + bool DispatchAttachmentBrokerMessage(const Message& message); + // Dispatches messages from queued_messages_ to listeners. Successfully // dispatched messages are removed from queued_messages_. DispatchState DispatchMessages(); @@ -171,6 +198,11 @@ class IPC_EXPORT ChannelReader : public SupportsAttachmentBrokering, // this buffer. std::string input_overflow_buf_; + // Maximum overflow buffer size, see Channel::kMaximumReadBufferSize. + // This is not a constant because we update it to reflect the reality + // of std::string::reserve() implementation. + size_t max_input_buffer_size_; + // These messages are waiting to be dispatched. If this vector is non-empty, // then the front Message must be blocked on receiving an attachment from the // AttachmentBroker. diff --git a/chromium/ipc/ipc_channel_reader_unittest.cc b/chromium/ipc/ipc_channel_reader_unittest.cc index 7728a817377..4ec71b53676 100644 --- a/chromium/ipc/ipc_channel_reader_unittest.cc +++ b/chromium/ipc/ipc_channel_reader_unittest.cc @@ -4,15 +4,27 @@ #include "build/build_config.h" +#include <stddef.h> +#include <stdint.h> + #include <limits> #include <set> +#include "base/run_loop.h" #include "ipc/attachment_broker.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_channel_reader.h" #include "ipc/placeholder_brokerable_attachment.h" #include "testing/gtest/include/gtest/gtest.h" +// Whether IPC::Message::FindNext() can determine message size for +// partial messages. The condition is from FindNext() implementation. +#if USE_ATTACHMENT_BROKER +#define MESSAGE_FINDNEXT_PARTIAL 0 +#else +#define MESSAGE_FINDNEXT_PARTIAL 1 +#endif + namespace IPC { namespace internal { @@ -42,8 +54,9 @@ class MockAttachmentBroker : public AttachmentBroker { public: typedef std::set<scoped_refptr<BrokerableAttachment>> AttachmentSet; - bool SendAttachmentToProcess(const BrokerableAttachment* attachment, - base::ProcessId destination_process) override { + bool SendAttachmentToProcess( + const scoped_refptr<BrokerableAttachment>& attachment, + base::ProcessId destination_process) override { return false; } @@ -63,7 +76,14 @@ class MockChannelReader : public ChannelReader { : ChannelReader(nullptr), last_dispatched_message_(nullptr) {} ReadState ReadData(char* buffer, int buffer_len, int* bytes_read) override { - return READ_FAILED; + if (data_.empty()) + return READ_PENDING; + + size_t read_len = std::min(static_cast<size_t>(buffer_len), data_.size()); + memcpy(buffer, data_.data(), read_len); + *bytes_read = static_cast<int>(read_len); + data_.erase(0, read_len); + return READ_SUCCEEDED; } bool ShouldDispatchInputMessage(Message* msg) override { return true; } @@ -91,9 +111,18 @@ class MockChannelReader : public ChannelReader { void set_broker(AttachmentBroker* broker) { broker_ = broker; } + void AppendData(const void* data, size_t size) { + data_.append(static_cast<const char*>(data), size); + } + + void AppendMessageData(const Message& message) { + AppendData(message.data(), message.size()); + } + private: Message* last_dispatched_message_; AttachmentBroker* broker_; + std::string data_; }; class ExposedMessage: public Message { @@ -102,6 +131,9 @@ class ExposedMessage: public Message { using Message::header; }; +// Payload that makes messages large +const size_t LargePayloadSize = Channel::kMaximumReadBufferSize * 3 / 2; + } // namespace #if USE_ATTACHMENT_BROKER @@ -123,6 +155,8 @@ TEST(ChannelReaderTest, AttachmentAlreadyBrokered) { } TEST(ChannelReaderTest, AttachmentNotYetBrokered) { + scoped_ptr<base::MessageLoop> message_loop(new base::MessageLoopForIO()); + MockAttachmentBroker broker; MockChannelReader reader; reader.set_broker(&broker); @@ -138,6 +172,9 @@ TEST(ChannelReaderTest, AttachmentNotYetBrokered) { EXPECT_EQ(nullptr, reader.get_last_dispatched_message()); broker.AddAttachment(attachment); + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + EXPECT_EQ(m, reader.get_last_dispatched_message()); } @@ -182,7 +219,7 @@ TEST(ChannelReaderTest, InvalidMessageSize) { reinterpret_cast<const char*>(&header), sizeof(header))); EXPECT_LE(reader.input_overflow_buf_.capacity(), capacity_before); - // Payload size is maximum int32 value + // Payload size is maximum int32_t value header.payload_size = std::numeric_limits<int32_t>::max(); EXPECT_FALSE(reader.TranslateInputData( reinterpret_cast<const char*>(&header), sizeof(header))); @@ -191,5 +228,155 @@ TEST(ChannelReaderTest, InvalidMessageSize) { #endif // !USE_ATTACHMENT_BROKER +TEST(ChannelReaderTest, TrimBuffer) { + // ChannelReader uses std::string as a buffer, and calls reserve() + // to trim it to kMaximumReadBufferSize. However, an implementation + // is free to actually reserve a larger amount. + size_t trimmed_buffer_size; + { + std::string buf; + buf.reserve(Channel::kMaximumReadBufferSize); + trimmed_buffer_size = buf.capacity(); + } + + // Buffer is trimmed after message is processed. + { + MockChannelReader reader; + + Message message; + message.WriteString(std::string(LargePayloadSize, 'X')); + + // Sanity check + EXPECT_TRUE(message.size() > trimmed_buffer_size); + + // Initially buffer is small + EXPECT_LE(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); + + // Write and process large message + reader.AppendMessageData(message); + EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, + reader.ProcessIncomingMessages()); + + // After processing large message buffer is trimmed + EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); + } + + // Buffer is trimmed only after entire message is processed. + { + MockChannelReader reader; + + ExposedMessage message; + message.WriteString(std::string(LargePayloadSize, 'X')); + + // Write and process message header + reader.AppendData(message.header(), sizeof(ExposedMessage::Header)); + EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, + reader.ProcessIncomingMessages()); + +#if MESSAGE_FINDNEXT_PARTIAL + // We determined message size for the message from its header, so + // we resized the buffer to fit. + EXPECT_GE(reader.input_overflow_buf_.capacity(), message.size()); +#else + // We couldn't determine message size, so we didn't resize the buffer. +#endif + + // Write and process payload + reader.AppendData(message.payload(), message.payload_size()); + EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, + reader.ProcessIncomingMessages()); + + // But once we process the message, we trim the buffer + EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); + } + + // Buffer is not trimmed if the next message is also large. + { + MockChannelReader reader; + + // Write large message + Message message1; + message1.WriteString(std::string(LargePayloadSize * 2, 'X')); + reader.AppendMessageData(message1); + + // Write header for the next large message + ExposedMessage message2; + message2.WriteString(std::string(LargePayloadSize, 'Y')); + reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); + + // Process messages + EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, + reader.ProcessIncomingMessages()); + +#if MESSAGE_FINDNEXT_PARTIAL + // We determined message size for the second (partial) message, so + // we resized the buffer to fit. + EXPECT_GE(reader.input_overflow_buf_.capacity(), message1.size()); +#else + // We couldn't determine message size for the second (partial) message, + // so we trimmed the buffer. + EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); +#endif + } + + // Buffer resized appropriately if next message is larger than the first. + // (Similar to the test above except for the order of messages.) + { + MockChannelReader reader; + + // Write large message + Message message1; + message1.WriteString(std::string(LargePayloadSize, 'Y')); + reader.AppendMessageData(message1); + + // Write header for the next even larger message + ExposedMessage message2; + message2.WriteString(std::string(LargePayloadSize * 2, 'X')); + reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); + + // Process messages + EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, + reader.ProcessIncomingMessages()); + +#if MESSAGE_FINDNEXT_PARTIAL + // We determined message size for the second (partial) message, and + // resized the buffer to fit it. + EXPECT_GE(reader.input_overflow_buf_.capacity(), message2.size()); +#else + // We couldn't determine message size for the second (partial) message, + // so we trimmed the buffer. + EXPECT_EQ(reader.input_overflow_buf_.capacity(), trimmed_buffer_size); +#endif + } + + // Buffer is not trimmed if we've just resized it to accommodate large + // incoming message. + { + MockChannelReader reader; + + // Write small message + Message message1; + message1.WriteString(std::string(11, 'X')); + reader.AppendMessageData(message1); + + // Write header for the next large message + ExposedMessage message2; + message2.WriteString(std::string(LargePayloadSize, 'Y')); + reader.AppendData(message2.header(), sizeof(ExposedMessage::Header)); + + EXPECT_EQ(ChannelReader::DISPATCH_FINISHED, + reader.ProcessIncomingMessages()); + +#if MESSAGE_FINDNEXT_PARTIAL + // We determined message size for the second (partial) message, so + // we resized the buffer to fit. + EXPECT_GE(reader.input_overflow_buf_.capacity(), message2.size()); +#else + // We couldn't determine size for the second (partial) message, and + // first message was small, so we did nothing. +#endif + } +} + } // namespace internal } // namespace IPC diff --git a/chromium/ipc/ipc_channel_unittest.cc b/chromium/ipc/ipc_channel_unittest.cc index 0c444fff3ee..0a49ca9abe0 100644 --- a/chromium/ipc/ipc_channel_unittest.cc +++ b/chromium/ipc/ipc_channel_unittest.cc @@ -8,6 +8,8 @@ #include <windows.h> #endif +#include <stdint.h> + #include <string> #include "base/pickle.h" diff --git a/chromium/ipc/ipc_channel_win.cc b/chromium/ipc/ipc_channel_win.cc index e758d944d9e..62918627c17 100644 --- a/chromium/ipc/ipc_channel_win.cc +++ b/chromium/ipc/ipc_channel_win.cc @@ -4,8 +4,9 @@ #include "ipc/ipc_channel_win.h" -#include <stdint.h> #include <windows.h> +#include <stddef.h> +#include <stdint.h> #include "base/auto_reset.h" #include "base/bind.h" @@ -103,13 +104,12 @@ bool ChannelWin::Send(Message* message) { bool ChannelWin::ProcessMessageForDelivery(Message* message) { // Sending a brokerable attachment requires a call to Channel::Send(), so - // both Send() and ProcessMessageForDelivery() may be re-entrant. Brokered - // attachments must be sent before the Message itself. + // both Send() and ProcessMessageForDelivery() may be re-entrant. if (message->HasBrokerableAttachments()) { DCHECK(GetAttachmentBroker()); DCHECK(peer_pid_ != base::kNullProcessId); - for (const BrokerableAttachment* attachment : - message->attachment_set()->PeekBrokerableAttachments()) { + for (const scoped_refptr<IPC::BrokerableAttachment>& attachment : + message->attachment_set()->GetBrokerableAttachments()) { if (!GetAttachmentBroker()->SendAttachmentToProcess(attachment, peer_pid_)) { delete message; @@ -161,7 +161,17 @@ void ChannelWin::FlushPrelimQueue() { while (!prelim_queue.empty()) { Message* m = prelim_queue.front(); - ProcessMessageForDelivery(m); + bool success = ProcessMessageForDelivery(m); + prelim_queue.pop(); + + if (!success) + break; + } + + // Delete any unprocessed messages. + while (!prelim_queue.empty()) { + Message* m = prelim_queue.front(); + delete m; prelim_queue.pop(); } } @@ -254,9 +264,9 @@ void ChannelWin::HandleInternalMessage(const Message& msg) { // Validation completed. validate_client_ = false; - FlushPrelimQueue(); - listener()->OnChannelConnected(claimed_pid); + + FlushPrelimQueue(); } base::ProcessId ChannelWin::GetSenderPID() { diff --git a/chromium/ipc/ipc_channel_win.h b/chromium/ipc/ipc_channel_win.h index 35a158e3c6d..fd186fc186f 100644 --- a/chromium/ipc/ipc_channel_win.h +++ b/chromium/ipc/ipc_channel_win.h @@ -12,6 +12,7 @@ #include <queue> #include <string> +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" diff --git a/chromium/ipc/ipc_fuzzing_tests.cc b/chromium/ipc/ipc_fuzzing_tests.cc index 769922d036f..6eb93054c66 100644 --- a/chromium/ipc/ipc_fuzzing_tests.cc +++ b/chromium/ipc/ipc_fuzzing_tests.cc @@ -13,6 +13,7 @@ #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/platform_thread.h" +#include "build/build_config.h" #include "ipc/ipc_test_base.h" #include "testing/gtest/include/gtest/gtest.h" @@ -185,7 +186,7 @@ class FuzzerServerListener : public SimpleListener { --message_count_; --pending_messages_; if (0 == message_count_) - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } void ReplyMsgNotHandled(uint32_t type_id) { @@ -212,7 +213,7 @@ class FuzzerClientListener : public SimpleListener { bool OnMessageReceived(const IPC::Message& msg) override { last_msg_ = new IPC::Message(msg); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); return true; } diff --git a/chromium/ipc/ipc_logging.cc b/chromium/ipc/ipc_logging.cc index a9aa0060c08..abe26cb82d7 100644 --- a/chromium/ipc/ipc_logging.cc +++ b/chromium/ipc/ipc_logging.cc @@ -8,6 +8,9 @@ #define IPC_MESSAGE_MACROS_LOG_ENABLED #endif +#include <stddef.h> +#include <stdint.h> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" @@ -19,6 +22,7 @@ #include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" #include "base/time/time.h" +#include "build/build_config.h" #include "ipc/ipc_message_utils.h" #include "ipc/ipc_sender.h" #include "ipc/ipc_switches.h" diff --git a/chromium/ipc/ipc_message.cc b/chromium/ipc/ipc_message.cc index df28464e234..71f72997452 100644 --- a/chromium/ipc/ipc_message.cc +++ b/chromium/ipc/ipc_message.cc @@ -5,10 +5,13 @@ #include "ipc/ipc_message.h" #include <limits.h> +#include <stddef.h> +#include <stdint.h> #include "base/atomic_sequence_num.h" #include "base/logging.h" #include "build/build_config.h" +#include "ipc/attachment_broker.h" #include "ipc/ipc_message_attachment.h" #include "ipc/ipc_message_attachment_set.h" #include "ipc/placeholder_brokerable_attachment.h" @@ -49,7 +52,7 @@ Message::~Message() { Message::Message() : base::Pickle(sizeof(Header)) { header()->routing = header()->type = 0; header()->flags = GetRefNumUpper24(); -#if defined(OS_MACOSX) +#if USE_ATTACHMENT_BROKER header()->num_brokered_attachments = 0; #endif #if defined(OS_POSIX) @@ -65,7 +68,7 @@ Message::Message(int32_t routing_id, uint32_t type, PriorityValue priority) header()->type = type; DCHECK((priority & 0xffffff00) == 0); header()->flags = priority | GetRefNumUpper24(); -#if defined(OS_MACOSX) +#if USE_ATTACHMENT_BROKER header()->num_brokered_attachments = 0; #endif #if defined(OS_POSIX) @@ -83,6 +86,7 @@ Message::Message(const char* data, int data_len) Message::Message(const Message& other) : base::Pickle(other) { Init(); attachment_set_ = other.attachment_set_; + sender_pid_ = other.sender_pid_; } void Message::Init() { @@ -98,6 +102,7 @@ void Message::Init() { Message& Message::operator=(const Message& other) { *static_cast<base::Pickle*>(this) = other; attachment_set_ = other.attachment_set_; + sender_pid_ = other.sender_pid_; return *this; } @@ -144,16 +149,15 @@ Message::NextMessageInfo::~NextMessageInfo() {} Message::SerializedAttachmentIds Message::SerializedIdsOfBrokerableAttachments() { DCHECK(HasBrokerableAttachments()); - std::vector<const BrokerableAttachment*> attachments = - attachment_set_->PeekBrokerableAttachments(); + std::vector<scoped_refptr<IPC::BrokerableAttachment>> attachments( + attachment_set_->GetBrokerableAttachments()); CHECK_LE(attachments.size(), std::numeric_limits<size_t>::max() / BrokerableAttachment::kNonceSize); size_t size = attachments.size() * BrokerableAttachment::kNonceSize; char* buffer = static_cast<char*>(malloc(size)); for (size_t i = 0; i < attachments.size(); ++i) { - const BrokerableAttachment* attachment = attachments[i]; char* start_range = buffer + i * BrokerableAttachment::kNonceSize; - BrokerableAttachment::AttachmentId id = attachment->GetIdentifier(); + BrokerableAttachment::AttachmentId id = attachments[i]->GetIdentifier(); id.SerializeToBuffer(start_range, BrokerableAttachment::kNonceSize); } SerializedAttachmentIds ids; @@ -204,7 +208,7 @@ void Message::FindNext(const char* range_start, if (buffer_length < attachment_length + pickle_size) return; - for (int i = 0; i < num_attachments; ++i) { + for (size_t i = 0; i < num_attachments; ++i) { const char* attachment_start = pickle_end + i * BrokerableAttachment::kNonceSize; BrokerableAttachment::AttachmentId id(attachment_start, @@ -237,24 +241,46 @@ bool Message::AddPlaceholderBrokerableAttachmentWithId( } bool Message::WriteAttachment(scoped_refptr<MessageAttachment> attachment) { - // We write the index of the descriptor so that we don't have to + bool brokerable; + size_t index; + bool success = + attachment_set()->AddAttachment(attachment, &index, &brokerable); + DCHECK(success); + + // Write the type of descriptor. + WriteBool(brokerable); + + // Write the index of the descriptor so that we don't have to // keep the current descriptor as extra decoding state when deserialising. - WriteInt(attachment_set()->size()); - return attachment_set()->AddAttachment(attachment); + WriteInt(static_cast<int>(index)); + +#if USE_ATTACHMENT_BROKER + if (brokerable) + header()->num_brokered_attachments++; +#endif + + return success; } bool Message::ReadAttachment( base::PickleIterator* iter, scoped_refptr<MessageAttachment>* attachment) const { - int descriptor_index; - if (!iter->ReadInt(&descriptor_index)) + bool brokerable; + if (!iter->ReadBool(&brokerable)) + return false; + + int index; + if (!iter->ReadInt(&index)) return false; MessageAttachmentSet* attachment_set = attachment_set_.get(); if (!attachment_set) return false; - *attachment = attachment_set->GetAttachmentAt(descriptor_index); + *attachment = brokerable + ? attachment_set->GetBrokerableAttachmentAt(index) + : attachment_set->GetNonBrokerableAttachmentAt(index); + return nullptr != attachment->get(); } diff --git a/chromium/ipc/ipc_message.h b/chromium/ipc/ipc_message.h index 22d1c99da98..a6c641d7104 100644 --- a/chromium/ipc/ipc_message.h +++ b/chromium/ipc/ipc_message.h @@ -5,6 +5,7 @@ #ifndef IPC_IPC_MESSAGE_H_ #define IPC_IPC_MESSAGE_H_ +#include <stddef.h> #include <stdint.h> #include <string> @@ -13,6 +14,8 @@ #include "base/memory/ref_counted.h" #include "base/pickle.h" #include "base/trace_event/trace_event.h" +#include "build/build_config.h" +#include "ipc/attachment_broker.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_export.h" @@ -268,7 +271,7 @@ class IPC_EXPORT Message : public base::Pickle { int32_t routing; // ID of the view that this message is destined for uint32_t type; // specifies the user-defined message type uint32_t flags; // specifies control flags for the message -#if defined(OS_MACOSX) +#if USE_ATTACHMENT_BROKER // The number of brokered attachments included with this message. The // ids of the brokered attachment ids are sent immediately after the pickled // message, before the next pickled message is sent. diff --git a/chromium/ipc/ipc_message_attachment.h b/chromium/ipc/ipc_message_attachment.h index e7553d262cf..dda06300012 100644 --- a/chromium/ipc/ipc_message_attachment.h +++ b/chromium/ipc/ipc_message_attachment.h @@ -8,6 +8,7 @@ #include "base/files/file.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "build/build_config.h" #include "ipc/ipc_export.h" namespace IPC { @@ -15,7 +16,7 @@ namespace IPC { // Auxiliary data sent with |Message|. This can be a platform file descriptor // or a mojo |MessagePipe|. |GetType()| returns the type of the subclass. class IPC_EXPORT MessageAttachment - : public base::RefCounted<MessageAttachment> { + : public base::RefCountedThreadSafe<MessageAttachment> { public: enum Type { TYPE_PLATFORM_FILE, // The instance is |PlatformFileAttachment|. @@ -30,7 +31,7 @@ class IPC_EXPORT MessageAttachment #endif // OS_POSIX protected: - friend class base::RefCounted<MessageAttachment>; + friend class base::RefCountedThreadSafe<MessageAttachment>; MessageAttachment(); virtual ~MessageAttachment(); diff --git a/chromium/ipc/ipc_message_attachment_set.cc b/chromium/ipc/ipc_message_attachment_set.cc index 6a8024fe9e6..3b7eefb9a6e 100644 --- a/chromium/ipc/ipc_message_attachment_set.cc +++ b/chromium/ipc/ipc_message_attachment_set.cc @@ -4,15 +4,19 @@ #include "ipc/ipc_message_attachment_set.h" +#include <stddef.h> + #include <algorithm> + #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_message_attachment.h" #if defined(OS_POSIX) -#include <sys/types.h> #include <sys/stat.h> +#include <sys/types.h> #include <unistd.h> #include "ipc/ipc_platform_file_attachment_posix.h" #endif // OS_POSIX @@ -39,7 +43,7 @@ MessageAttachmentSet::MessageAttachmentSet() } MessageAttachmentSet::~MessageAttachmentSet() { - if (consumed_descriptor_highwater_ == size()) + if (consumed_descriptor_highwater_ == num_non_brokerable_attachments()) return; // We close all the owning descriptors. If this message should have @@ -51,7 +55,7 @@ MessageAttachmentSet::~MessageAttachmentSet() { // the descriptors have their close flag set and we free all the extra // kernel resources. LOG(WARNING) << "MessageAttachmentSet destroyed with unconsumed descriptors: " - << consumed_descriptor_highwater_ << "/" << size(); + << consumed_descriptor_highwater_ << "/" << num_descriptors(); } unsigned MessageAttachmentSet::num_descriptors() const { @@ -65,16 +69,22 @@ unsigned MessageAttachmentSet::num_mojo_handles() const { } unsigned MessageAttachmentSet::num_brokerable_attachments() const { - return count_attachments_of_type( - attachments_, MessageAttachment::TYPE_BROKERABLE_ATTACHMENT); + return static_cast<unsigned>(brokerable_attachments_.size()); } -unsigned MessageAttachmentSet::size() const { +unsigned MessageAttachmentSet::num_non_brokerable_attachments() const { return static_cast<unsigned>(attachments_.size()); } +unsigned MessageAttachmentSet::size() const { + return static_cast<unsigned>(attachments_.size() + + brokerable_attachments_.size()); +} + bool MessageAttachmentSet::AddAttachment( - scoped_refptr<MessageAttachment> attachment) { + scoped_refptr<MessageAttachment> attachment, + size_t* index, + bool* brokerable) { #if defined(OS_POSIX) if (attachment->GetType() == MessageAttachment::TYPE_PLATFORM_FILE && num_descriptors() == kMaxDescriptorsPerMessage) { @@ -83,14 +93,37 @@ bool MessageAttachmentSet::AddAttachment( } #endif - attachments_.push_back(attachment); - return true; + switch (attachment->GetType()) { + case MessageAttachment::TYPE_PLATFORM_FILE: + case MessageAttachment::TYPE_MOJO_HANDLE: + attachments_.push_back(attachment); + *index = attachments_.size() - 1; + *brokerable = false; + return true; + case MessageAttachment::TYPE_BROKERABLE_ATTACHMENT: + BrokerableAttachment* brokerable_attachment = + static_cast<BrokerableAttachment*>(attachment.get()); + scoped_refptr<BrokerableAttachment> a(brokerable_attachment); + brokerable_attachments_.push_back(a); + *index = brokerable_attachments_.size() - 1; + *brokerable = true; + return true; + } + return false; } -scoped_refptr<MessageAttachment> MessageAttachmentSet::GetAttachmentAt( - unsigned index) { - if (index >= size()) { - DLOG(WARNING) << "Accessing out of bound index:" << index << "/" << size(); +bool MessageAttachmentSet::AddAttachment( + scoped_refptr<MessageAttachment> attachment) { + bool brokerable; + size_t index; + return AddAttachment(attachment, &index, &brokerable); +} + +scoped_refptr<MessageAttachment> +MessageAttachmentSet::GetNonBrokerableAttachmentAt(unsigned index) { + if (index >= num_non_brokerable_attachments()) { + DLOG(WARNING) << "Accessing out of bound index:" << index << "/" + << num_non_brokerable_attachments(); return scoped_refptr<MessageAttachment>(); } @@ -115,8 +148,10 @@ scoped_refptr<MessageAttachment> MessageAttachmentSet::GetAttachmentAt( // end of the array and index 0 is requested, we reset the highwater value. // TODO(morrita): This is absurd. This "wringle" disallow to introduce clearer // ownership model. Only client is NaclIPCAdapter. See crbug.com/415294 - if (index == 0 && consumed_descriptor_highwater_ == size()) + if (index == 0 && + consumed_descriptor_highwater_ == num_non_brokerable_attachments()) { consumed_descriptor_highwater_ = 0; + } if (index != consumed_descriptor_highwater_) return scoped_refptr<MessageAttachment>(); @@ -126,34 +161,36 @@ scoped_refptr<MessageAttachment> MessageAttachmentSet::GetAttachmentAt( return attachments_[index]; } -void MessageAttachmentSet::CommitAll() { +scoped_refptr<MessageAttachment> +MessageAttachmentSet::GetBrokerableAttachmentAt(unsigned index) { + if (index >= num_brokerable_attachments()) { + DLOG(WARNING) << "Accessing out of bound index:" << index << "/" + << num_brokerable_attachments(); + return scoped_refptr<MessageAttachment>(); + } + + scoped_refptr<BrokerableAttachment> brokerable_attachment( + brokerable_attachments_[index]); + return scoped_refptr<MessageAttachment>(brokerable_attachment.get()); +} + +void MessageAttachmentSet::CommitAllDescriptors() { attachments_.clear(); consumed_descriptor_highwater_ = 0; } -std::vector<const BrokerableAttachment*> -MessageAttachmentSet::PeekBrokerableAttachments() const { - std::vector<const BrokerableAttachment*> output; - for (const scoped_refptr<MessageAttachment>& attachment : attachments_) { - if (attachment->GetType() == - MessageAttachment::TYPE_BROKERABLE_ATTACHMENT) { - output.push_back(static_cast<BrokerableAttachment*>(attachment.get())); - } - } - return output; +std::vector<scoped_refptr<IPC::BrokerableAttachment>> +MessageAttachmentSet::GetBrokerableAttachments() const { + return brokerable_attachments_; } void MessageAttachmentSet::ReplacePlaceholderWithAttachment( const scoped_refptr<BrokerableAttachment>& attachment) { - for (auto it = attachments_.begin(); it != attachments_.end(); ++it) { - if ((*it)->GetType() != MessageAttachment::TYPE_BROKERABLE_ATTACHMENT) - continue; - BrokerableAttachment* brokerable_attachment = - static_cast<BrokerableAttachment*>(it->get()); - - if (brokerable_attachment->GetBrokerableType() == - BrokerableAttachment::PLACEHOLDER && - brokerable_attachment->GetIdentifier() == attachment->GetIdentifier()) { + DCHECK_NE(BrokerableAttachment::PLACEHOLDER, attachment->GetBrokerableType()); + for (auto it = brokerable_attachments_.begin(); + it != brokerable_attachments_.end(); ++it) { + if ((*it)->GetBrokerableType() == BrokerableAttachment::PLACEHOLDER && + (*it)->GetIdentifier() == attachment->GetIdentifier()) { *it = attachment; return; } @@ -191,7 +228,7 @@ void MessageAttachmentSet::ReleaseFDsToClose( fds->push_back(file->TakePlatformFile()); } - CommitAll(); + CommitAllDescriptors(); } void MessageAttachmentSet::AddDescriptorsToOwn(const base::PlatformFile* buffer, diff --git a/chromium/ipc/ipc_message_attachment_set.h b/chromium/ipc/ipc_message_attachment_set.h index 4707a504e3c..764c818c9f0 100644 --- a/chromium/ipc/ipc_message_attachment_set.h +++ b/chromium/ipc/ipc_message_attachment_set.h @@ -5,10 +5,13 @@ #ifndef IPC_IPC_MESSAGE_ATTACHMENT_SET_H_ #define IPC_IPC_MESSAGE_ATTACHMENT_SET_H_ +#include <stddef.h> + #include <vector> #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "build/build_config.h" #include "ipc/ipc_export.h" #if defined(OS_POSIX) @@ -21,10 +24,21 @@ class BrokerableAttachment; class MessageAttachment; // ----------------------------------------------------------------------------- -// A MessageAttachmentSet is an ordered set of MessageAttachment objects. These -// are associated with IPC messages so that attachments, each of which is either -// a platform file or a mojo handle, can be transmitted over the underlying UNIX -// domain socket (for ChannelPosix) or Mojo MessagePipe (for ChannelMojo). +// A MessageAttachmentSet is an ordered set of MessageAttachment objects +// associated with an IPC message. There are three types of MessageAttachments: +// 1) TYPE_PLATFORM_FILE is transmitted over the Channel's underlying +// UNIX domain socket +// 2) TYPE_MOJO_HANDLE is transmitted over the Mojo MessagePipe. +// 3) TYPE_BROKERABLE_ATTACHMENT is transmitted by the Attachment Broker. +// Any given IPC Message can have attachments of type (1) or (2), but not both. +// These are stored in |attachments_|. Attachments of type (3) are stored in +// |brokerable_attachments_|. +// +// To produce a deterministic ordering, all attachments in |attachments_| are +// considered to come before those in |brokerable_attachments_|. These +// attachments are transmitted across different communication channels, and +// multiplexed by the receiver, so ordering between them cannot be guaranteed. +// // ----------------------------------------------------------------------------- class IPC_EXPORT MessageAttachmentSet : public base::RefCountedThreadSafe<MessageAttachmentSet> { @@ -39,27 +53,45 @@ class IPC_EXPORT MessageAttachmentSet unsigned num_mojo_handles() const; // Return the number of brokerable attachments in the attachment set. unsigned num_brokerable_attachments() const; + // Return the number of non-brokerable attachments in the attachment set. + unsigned num_non_brokerable_attachments() const; // Return true if no unconsumed descriptors remain bool empty() const { return 0 == size(); } + // Returns whether the attachment was successfully added. + // |index| is an output variable. On success, it contains the index of the + // newly added attachment. + // |brokerable| is an output variable. On success, it describes which vector + // the attachment was added to. + bool AddAttachment(scoped_refptr<MessageAttachment> attachment, + size_t* index, + bool* brokerable); + + // Similar to the above method, but without output variables. bool AddAttachment(scoped_refptr<MessageAttachment> attachment); - // Take the nth attachment from the beginning of the set, Code using this - // /must/ access the attachments in order, and must do it at most once. + // Take the nth non-brokerable attachment from the beginning of the vector, + // Code using this /must/ access the attachments in order, and must do it at + // most once. // // This interface is designed for the deserialising code as it doesn't // support close flags. // returns: an attachment, or nullptr on error - scoped_refptr<MessageAttachment> GetAttachmentAt(unsigned index); + scoped_refptr<MessageAttachment> GetNonBrokerableAttachmentAt(unsigned index); + + // Similar to GetNonBrokerableAttachmentAt, but there are no ordering + // requirements. + scoped_refptr<MessageAttachment> GetBrokerableAttachmentAt(unsigned index); // This must be called after transmitting the descriptors returned by - // PeekDescriptors. It marks all the descriptors as consumed and closes those - // which are auto-close. - void CommitAll(); + // PeekDescriptors. It marks all the non-brokerable descriptors as consumed + // and closes those which are auto-close. + void CommitAllDescriptors(); // Returns a vector of all brokerable attachments. - std::vector<const BrokerableAttachment*> PeekBrokerableAttachments() const; + std::vector<scoped_refptr<IPC::BrokerableAttachment>> + GetBrokerableAttachments() const; // Replaces a placeholder brokerable attachment with |attachment|, matching // them by their id. @@ -80,15 +112,16 @@ class IPC_EXPORT MessageAttachmentSet // --------------------------------------------------------------------------- // Interfaces for transmission... - // Fill an array with file descriptors without 'consuming' them. CommitAll - // must be called after these descriptors have been transmitted. + // Fill an array with file descriptors without 'consuming' them. + // CommitAllDescriptors must be called after these descriptors have been + // transmitted. // buffer: (output) a buffer of, at least, size() integers. void PeekDescriptors(base::PlatformFile* buffer) const; // Returns true if any contained file descriptors appear to be handles to a // directory. bool ContainsDirectoryDescriptor() const; - // Fetch all filedescriptors with the "auto close" property. - // Used instead of CommitAll() when closing must be handled manually. + // Fetch all filedescriptors with the "auto close" property. Used instead of + // CommitAllDescriptors() when closing must be handled manually. void ReleaseFDsToClose(std::vector<base::PlatformFile>* fds); // --------------------------------------------------------------------------- @@ -110,10 +143,12 @@ class IPC_EXPORT MessageAttachmentSet ~MessageAttachmentSet(); - // A vector of attachments of the message, which might be |PlatformFile| or - // |MessagePipe|. + // All elements either have type TYPE_PLATFORM_FILE or TYPE_MOJO_HANDLE. std::vector<scoped_refptr<MessageAttachment>> attachments_; + // All elements have type TYPE_BROKERABLE_ATTACHMENT. + std::vector<scoped_refptr<BrokerableAttachment>> brokerable_attachments_; + // This contains the index of the next descriptor which should be consumed. // It's used in a couple of ways. Firstly, at destruction we can check that // all the descriptors have been read (with GetNthDescriptor). Secondly, we diff --git a/chromium/ipc/ipc_message_attachment_set_posix_unittest.cc b/chromium/ipc/ipc_message_attachment_set_posix_unittest.cc index 8df312f1d37..e43e43163cb 100644 --- a/chromium/ipc/ipc_message_attachment_set_posix_unittest.cc +++ b/chromium/ipc/ipc_message_attachment_set_posix_unittest.cc @@ -7,9 +7,11 @@ #include "ipc/ipc_message_attachment_set.h" #include <fcntl.h> +#include <stddef.h> #include <unistd.h> #include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" #include "ipc/ipc_platform_file_attachment_posix.h" #include "testing/gtest/include/gtest/gtest.h" @@ -49,7 +51,7 @@ TEST(MessageAttachmentSet, BasicAdd) { // Empties the set and stops a warning about deleting a set with unconsumed // descriptors - set->CommitAll(); + set->CommitAllDescriptors(); } TEST(MessageAttachmentSet, BasicAddAndClose) { @@ -63,7 +65,7 @@ TEST(MessageAttachmentSet, BasicAddAndClose) { ASSERT_EQ(set->size(), 1u); ASSERT_TRUE(!set->empty()); - set->CommitAll(); + set->CommitAllDescriptors(); ASSERT_TRUE(VerifyClosed(fd)); } @@ -77,7 +79,7 @@ TEST(MessageAttachmentSet, MaxSize) { ASSERT_TRUE( !set->AddAttachment(new internal::PlatformFileAttachment(kFDBase))); - set->CommitAll(); + set->CommitAllDescriptors(); } #if defined(OS_ANDROID) @@ -98,7 +100,7 @@ TEST(MessageAttachmentSet, MAYBE_SetDescriptors) { ASSERT_TRUE(!set->empty()); ASSERT_EQ(set->size(), 1u); - set->CommitAll(); + set->CommitAllDescriptors(); ASSERT_TRUE(VerifyClosed(fd)); } @@ -114,7 +116,7 @@ TEST(MessageAttachmentSet, PeekDescriptors) { fds[0] = 0; set->PeekDescriptors(fds); ASSERT_EQ(fds[0], kFDBase); - set->CommitAll(); + set->CommitAllDescriptors(); ASSERT_TRUE(set->empty()); } @@ -130,11 +132,13 @@ TEST(MessageAttachmentSet, WalkInOrder) { ASSERT_TRUE( set->AddAttachment(new internal::PlatformFileAttachment(kFDBase + 2))); - ASSERT_EQ(set->GetAttachmentAt(0)->TakePlatformFile(), kFDBase); - ASSERT_EQ(set->GetAttachmentAt(1)->TakePlatformFile(), kFDBase + 1); - ASSERT_EQ(set->GetAttachmentAt(2)->TakePlatformFile(), kFDBase + 2); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(0)->TakePlatformFile(), kFDBase); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(1)->TakePlatformFile(), + kFDBase + 1); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(2)->TakePlatformFile(), + kFDBase + 2); - set->CommitAll(); + set->CommitAllDescriptors(); } TEST(MessageAttachmentSet, WalkWrongOrder) { @@ -149,10 +153,10 @@ TEST(MessageAttachmentSet, WalkWrongOrder) { ASSERT_TRUE( set->AddAttachment(new internal::PlatformFileAttachment(kFDBase + 2))); - ASSERT_EQ(set->GetAttachmentAt(0)->TakePlatformFile(), kFDBase); - ASSERT_EQ(set->GetAttachmentAt(2), nullptr); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(0)->TakePlatformFile(), kFDBase); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(2), nullptr); - set->CommitAll(); + set->CommitAllDescriptors(); } TEST(MessageAttachmentSet, WalkCycle) { @@ -167,17 +171,23 @@ TEST(MessageAttachmentSet, WalkCycle) { ASSERT_TRUE( set->AddAttachment(new internal::PlatformFileAttachment(kFDBase + 2))); - ASSERT_EQ(set->GetAttachmentAt(0)->TakePlatformFile(), kFDBase); - ASSERT_EQ(set->GetAttachmentAt(1)->TakePlatformFile(), kFDBase + 1); - ASSERT_EQ(set->GetAttachmentAt(2)->TakePlatformFile(), kFDBase + 2); - ASSERT_EQ(set->GetAttachmentAt(0)->TakePlatformFile(), kFDBase); - ASSERT_EQ(set->GetAttachmentAt(1)->TakePlatformFile(), kFDBase + 1); - ASSERT_EQ(set->GetAttachmentAt(2)->TakePlatformFile(), kFDBase + 2); - ASSERT_EQ(set->GetAttachmentAt(0)->TakePlatformFile(), kFDBase); - ASSERT_EQ(set->GetAttachmentAt(1)->TakePlatformFile(), kFDBase + 1); - ASSERT_EQ(set->GetAttachmentAt(2)->TakePlatformFile(), kFDBase + 2); - - set->CommitAll(); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(0)->TakePlatformFile(), kFDBase); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(1)->TakePlatformFile(), + kFDBase + 1); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(2)->TakePlatformFile(), + kFDBase + 2); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(0)->TakePlatformFile(), kFDBase); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(1)->TakePlatformFile(), + kFDBase + 1); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(2)->TakePlatformFile(), + kFDBase + 2); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(0)->TakePlatformFile(), kFDBase); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(1)->TakePlatformFile(), + kFDBase + 1); + ASSERT_EQ(set->GetNonBrokerableAttachmentAt(2)->TakePlatformFile(), + kFDBase + 2); + + set->CommitAllDescriptors(); } #if defined(OS_ANDROID) @@ -190,7 +200,7 @@ TEST(MessageAttachmentSet, MAYBE_DontClose) { const int fd = GetSafeFd(); ASSERT_TRUE(set->AddAttachment(new internal::PlatformFileAttachment(fd))); - set->CommitAll(); + set->CommitAllDescriptors(); ASSERT_FALSE(VerifyClosed(fd)); } @@ -201,7 +211,7 @@ TEST(MessageAttachmentSet, DoClose) { const int fd = GetSafeFd(); ASSERT_TRUE(set->AddAttachment( new internal::PlatformFileAttachment(base::ScopedFD(fd)))); - set->CommitAll(); + set->CommitAllDescriptors(); ASSERT_TRUE(VerifyClosed(fd)); } diff --git a/chromium/ipc/ipc_message_macros.h b/chromium/ipc/ipc_message_macros.h index daeb617c452..f9fc28e6c94 100644 --- a/chromium/ipc/ipc_message_macros.h +++ b/chromium/ipc/ipc_message_macros.h @@ -444,7 +444,7 @@ Method func) { \ Schema::Param p; \ if (Read(msg, &p)) { \ - DispatchToMethod(obj, func, p); \ + base::DispatchToMethod(obj, func, p); \ return true; \ } \ return false; \ diff --git a/chromium/ipc/ipc_message_start.h b/chromium/ipc/ipc_message_start.h index 26ce0451fb3..8197ac62970 100644 --- a/chromium/ipc/ipc_message_start.h +++ b/chromium/ipc/ipc_message_start.h @@ -79,7 +79,6 @@ enum IPCMessageStart { WebSocketMsgStart, NaClHostMsgStart, WebRTCIdentityMsgStart, - LocalDiscoveryMsgStart, PowerMonitorMsgStart, EncryptedMediaMsgStart, CacheStorageMsgStart, @@ -113,6 +112,7 @@ enum IPCMessageStart { BluetoothMsgStart, CastMediaMsgStart, AwMessagePortMsgStart, + SyncCompositorMsgStart, ExtensionsGuestViewMsgStart, GuestViewMsgStart, // Note: CastCryptoMsgStart and CastChannelMsgStart reserved for Chromecast @@ -127,7 +127,14 @@ enum IPCMessageStart { RenderProcessMsgStart, PageLoadMetricsMsgStart, MemoryMsgStart, + MediaSessionMsgStart, IPCTestMsgStart, + ArcInstanceMsgStart, + ArcInstanceHostMsgStart, + DistillerMsgStart, + StartupMetricMsgStart, + ArcCameraMsgStart, + DWriteFontProxyMsgStart, LastIPCMsgStart // Must come last. }; diff --git a/chromium/ipc/ipc_message_unittest.cc b/chromium/ipc/ipc_message_unittest.cc index a4f9af07244..c022f9765a8 100644 --- a/chromium/ipc/ipc_message_unittest.cc +++ b/chromium/ipc/ipc_message_unittest.cc @@ -4,6 +4,8 @@ #include "ipc/ipc_message.h" +#include <stddef.h> +#include <stdint.h> #include <string.h> #include <limits> @@ -11,6 +13,8 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" +#include "build/build_config.h" +#include "ipc/attachment_broker.h" #include "ipc/ipc_message_utils.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/ipc/ipc_message_utils.cc b/chromium/ipc/ipc_message_utils.cc index 3baf2ee8a52..ea88b55a2a5 100644 --- a/chromium/ipc/ipc_message_utils.cc +++ b/chromium/ipc/ipc_message_utils.cc @@ -4,6 +4,9 @@ #include "ipc/ipc_message_utils.h" +#include <stddef.h> +#include <stdint.h> + #include "base/files/file_path.h" #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" @@ -12,6 +15,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "base/values.h" +#include "build/build_config.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_message_attachment.h" #include "ipc/ipc_message_attachment_set.h" @@ -24,6 +28,10 @@ #include "base/memory/shared_memory_handle.h" #endif // (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) +#if defined(OS_MACOSX) && !defined(OS_IOS) +#include "ipc/mach_port_mac.h" +#endif + #if defined(OS_WIN) #include <tchar.h> #include "ipc/handle_win.h" @@ -270,6 +278,24 @@ void ParamTraits<bool>::Log(const param_type& p, std::string* l) { l->append(p ? "true" : "false"); } +void ParamTraits<signed char>::Write(Message* m, const param_type& p) { + m->WriteBytes(&p, sizeof(param_type)); +} + +bool ParamTraits<signed char>::Read(const Message* m, + base::PickleIterator* iter, + param_type* r) { + const char* data; + if (!iter->ReadBytes(&data, sizeof(param_type))) + return false; + memcpy(r, data, sizeof(param_type)); + return true; +} + +void ParamTraits<signed char>::Log(const param_type& p, std::string* l) { + l->append(base::IntToString(p)); +} + void ParamTraits<unsigned char>::Write(Message* m, const param_type& p) { m->WriteBytes(&p, sizeof(param_type)); } @@ -553,7 +579,18 @@ void ParamTraits<base::SharedMemoryHandle>::Write(Message* m, ParamTraits<base::FileDescriptor>::Write(m, p.GetFileDescriptor()); break; case base::SharedMemoryHandle::MACH: - // TODO(erikchen): Implement me. http://crbug.com/535711 + MachPortMac mach_port_mac(p.GetMemoryObject()); + ParamTraits<MachPortMac>::Write(m, mach_port_mac); + size_t size = 0; + bool result = p.GetSize(&size); + DCHECK(result); + ParamTraits<size_t>::Write(m, size); + + // If the caller intended to pass ownership to the IPC stack, release a + // reference. + if (p.OwnershipPassesToIPC()) + p.Close(); + break; } } @@ -591,7 +628,16 @@ bool ParamTraits<base::SharedMemoryHandle>::Read(const Message* m, return true; } case base::SharedMemoryHandle::MACH: { - // TODO(erikchen): Implement me. http://crbug.com/535711 + MachPortMac mach_port_mac; + if (!ParamTraits<MachPortMac>::Read(m, iter, &mach_port_mac)) + return false; + + size_t size; + if (!ParamTraits<size_t>::Read(m, iter, &size)) + return false; + + *r = base::SharedMemoryHandle(mach_port_mac.get_mach_port(), size, + base::GetCurrentProcId()); return true; } } @@ -605,7 +651,8 @@ void ParamTraits<base::SharedMemoryHandle>::Log(const param_type& p, ParamTraits<base::FileDescriptor>::Log(p.GetFileDescriptor(), l); break; case base::SharedMemoryHandle::MACH: - // TODO(erikchen): Implement me. http://crbug.com/535711 + l->append("Mach port: "); + LogParam(p.GetMemoryObject(), l); break; } } @@ -613,9 +660,6 @@ void ParamTraits<base::SharedMemoryHandle>::Log(const param_type& p, #elif defined(OS_WIN) void ParamTraits<base::SharedMemoryHandle>::Write(Message* m, const param_type& p) { - // Longs on windows are 32 bits. - uint32_t pid = p.GetPID(); - m->WriteUInt32(pid); m->WriteBool(p.NeedsBrokering()); if (p.NeedsBrokering()) { @@ -629,11 +673,6 @@ void ParamTraits<base::SharedMemoryHandle>::Write(Message* m, bool ParamTraits<base::SharedMemoryHandle>::Read(const Message* m, base::PickleIterator* iter, param_type* r) { - uint32_t pid_int; - if (!iter->ReadUInt32(&pid_int)) - return false; - base::ProcessId pid = pid_int; - bool needs_brokering; if (!iter->ReadBool(&needs_brokering)) return false; @@ -642,7 +681,8 @@ bool ParamTraits<base::SharedMemoryHandle>::Read(const Message* m, HandleWin handle_win; if (!ParamTraits<HandleWin>::Read(m, iter, &handle_win)) return false; - *r = base::SharedMemoryHandle(handle_win.get_handle(), pid); + *r = base::SharedMemoryHandle(handle_win.get_handle(), + base::GetCurrentProcId()); return true; } @@ -650,14 +690,12 @@ bool ParamTraits<base::SharedMemoryHandle>::Read(const Message* m, if (!iter->ReadInt(&handle_int)) return false; HANDLE handle = LongToHandle(handle_int); - *r = base::SharedMemoryHandle(handle, pid); + *r = base::SharedMemoryHandle(handle, base::GetCurrentProcId()); return true; } void ParamTraits<base::SharedMemoryHandle>::Log(const param_type& p, std::string* l) { - LogParam(p.GetPID(), l); - l->append(" "); LogParam(p.GetHandle(), l); l->append(" needs brokering: "); LogParam(p.NeedsBrokering(), l); @@ -822,25 +860,6 @@ void ParamTraits<base::TimeTicks>::Log(const param_type& p, std::string* l) { ParamTraits<int64_t>::Log(p.ToInternalValue(), l); } -void ParamTraits<base::TraceTicks>::Write(Message* m, const param_type& p) { - ParamTraits<int64_t>::Write(m, p.ToInternalValue()); -} - -bool ParamTraits<base::TraceTicks>::Read(const Message* m, - base::PickleIterator* iter, - param_type* r) { - int64_t value; - bool ret = ParamTraits<int64_t>::Read(m, iter, &value); - if (ret) - *r = base::TraceTicks::FromInternalValue(value); - - return ret; -} - -void ParamTraits<base::TraceTicks>::Log(const param_type& p, std::string* l) { - ParamTraits<int64_t>::Log(p.ToInternalValue(), l); -} - void ParamTraits<IPC::ChannelHandle>::Write(Message* m, const param_type& p) { #if defined(OS_WIN) // On Windows marshalling pipe handle is not supported. @@ -966,7 +985,7 @@ bool ParamTraits<HANDLE>::Read(const Message* m, } void ParamTraits<HANDLE>::Log(const param_type& p, std::string* l) { - l->append(base::StringPrintf("0x%X", p)); + l->append(base::StringPrintf("0x%p", p)); } void ParamTraits<LOGFONT>::Write(Message* m, const param_type& p) { diff --git a/chromium/ipc/ipc_message_utils.h b/chromium/ipc/ipc_message_utils.h index 45f424d7633..69ea7cb6d8a 100644 --- a/chromium/ipc/ipc_message_utils.h +++ b/chromium/ipc/ipc_message_utils.h @@ -5,6 +5,8 @@ #ifndef IPC_IPC_MESSAGE_UTILS_H_ #define IPC_IPC_MESSAGE_UTILS_H_ +#include <limits.h> +#include <stddef.h> #include <stdint.h> #include <algorithm> @@ -23,6 +25,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/tuple.h" +#include "build/build_config.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_message_start.h" #include "ipc/ipc_param_traits.h" @@ -59,7 +62,6 @@ class NullableString16; class Time; class TimeDelta; class TimeTicks; -class TraceTicks; struct FileDescriptor; #if (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_WIN) @@ -133,6 +135,14 @@ struct ParamTraits<bool> { }; template <> +struct IPC_EXPORT ParamTraits<signed char> { + typedef signed char param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, base::PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + +template <> struct IPC_EXPORT ParamTraits<unsigned char> { typedef unsigned char param_type; static void Write(Message* m, const param_type& p); @@ -577,16 +587,6 @@ struct IPC_EXPORT ParamTraits<base::TimeTicks> { }; template <> -struct IPC_EXPORT ParamTraits<base::TraceTicks> { - typedef base::TraceTicks param_type; - static void Write(Message* m, const param_type& p); - static bool Read(const Message* m, - base::PickleIterator* iter, - param_type* r); - static void Log(const param_type& p, std::string* l); -}; - -template <> struct ParamTraits<base::Tuple<>> { typedef base::Tuple<> param_type; static void Write(Message* m, const param_type& p) { @@ -1026,7 +1026,7 @@ class SyncMessageSchema { Message* reply = SyncMessage::GenerateReply(msg); if (ok) { typename base::TupleTypes<ReplyParam>::ValueTuple reply_params; - DispatchToMethod(obj, func, send_params, &reply_params); + base::DispatchToMethod(obj, func, send_params, &reply_params); WriteParam(reply, reply_params); LogReplyParamsToMessage(reply_params, msg); } else { @@ -1046,7 +1046,7 @@ class SyncMessageSchema { if (ok) { base::Tuple<Message&> t = base::MakeRefTuple(*reply); ConnectMessageAndReply(msg, reply); - DispatchToMethod(obj, func, send_params, &t); + base::DispatchToMethod(obj, func, send_params, &t); } else { NOTREACHED() << "Error deserializing message " << msg->type(); reply->set_reply_error(); diff --git a/chromium/ipc/ipc_message_utils_unittest.cc b/chromium/ipc/ipc_message_utils_unittest.cc index 35a3e66271d..f3aa31a5160 100644 --- a/chromium/ipc/ipc_message_utils_unittest.cc +++ b/chromium/ipc/ipc_message_utils_unittest.cc @@ -4,6 +4,7 @@ #include "ipc/ipc_message_utils.h" +#include <stddef.h> #include <stdint.h> #include "base/files/file_path.h" diff --git a/chromium/ipc/ipc_perftest_support.cc b/chromium/ipc/ipc_perftest_support.cc index 3af0a1fa948..1ecc7c740e3 100644 --- a/chromium/ipc/ipc_perftest_support.cc +++ b/chromium/ipc/ipc_perftest_support.cc @@ -4,6 +4,9 @@ #include "ipc/ipc_perftest_support.h" +#include <stddef.h> +#include <stdint.h> + #include <algorithm> #include <string> @@ -348,7 +351,7 @@ scoped_refptr<base::TaskRunner> PingPongTestClient::task_runner() { LockThreadAffinity::LockThreadAffinity(int cpu_number) : affinity_set_ok_(false) { #if defined(OS_WIN) - const DWORD_PTR thread_mask = 1 << cpu_number; + const DWORD_PTR thread_mask = static_cast<DWORD_PTR>(1) << cpu_number; old_affinity_ = SetThreadAffinityMask(GetCurrentThread(), thread_mask); affinity_set_ok_ = old_affinity_ != 0; #elif defined(OS_LINUX) diff --git a/chromium/ipc/ipc_perftest_support.h b/chromium/ipc/ipc_perftest_support.h index 578256f8b11..80c58d127b9 100644 --- a/chromium/ipc/ipc_perftest_support.h +++ b/chromium/ipc/ipc_perftest_support.h @@ -5,8 +5,12 @@ #ifndef IPC_IPC_PERFTEST_SUPPORT_H_ #define IPC_IPC_PERFTEST_SUPPORT_H_ +#include <stddef.h> + #include <vector> +#include "base/macros.h" +#include "build/build_config.h" #include "ipc/ipc_test_base.h" namespace IPC { diff --git a/chromium/ipc/ipc_platform_file.cc b/chromium/ipc/ipc_platform_file.cc index 826d03014ed..97c176f3d7b 100644 --- a/chromium/ipc/ipc_platform_file.cc +++ b/chromium/ipc/ipc_platform_file.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "build/build_config.h" #include "ipc/ipc_platform_file.h" #if defined(OS_POSIX) diff --git a/chromium/ipc/ipc_platform_file.h b/chromium/ipc/ipc_platform_file.h index df6e8e394c0..fb4d0e423de 100644 --- a/chromium/ipc/ipc_platform_file.h +++ b/chromium/ipc/ipc_platform_file.h @@ -7,6 +7,7 @@ #include "base/files/file.h" #include "base/process/process.h" +#include "build/build_config.h" #include "ipc/ipc_export.h" #if defined(OS_POSIX) diff --git a/chromium/ipc/ipc_platform_file_attachment_posix.cc b/chromium/ipc/ipc_platform_file_attachment_posix.cc index b704750c156..b130ab26eb7 100644 --- a/chromium/ipc/ipc_platform_file_attachment_posix.cc +++ b/chromium/ipc/ipc_platform_file_attachment_posix.cc @@ -4,6 +4,8 @@ #include "ipc/ipc_platform_file_attachment_posix.h" +#include <utility> + namespace IPC { namespace internal { @@ -12,8 +14,7 @@ PlatformFileAttachment::PlatformFileAttachment(base::PlatformFile file) } PlatformFileAttachment::PlatformFileAttachment(base::ScopedFD file) - : file_(file.get()), owning_(file.Pass()) { -} + : file_(file.get()), owning_(std::move(file)) {} PlatformFileAttachment::~PlatformFileAttachment() { } diff --git a/chromium/ipc/ipc_send_fds_test.cc b/chromium/ipc/ipc_send_fds_test.cc index 22965da2d23..81f589412d6 100644 --- a/chromium/ipc/ipc_send_fds_test.cc +++ b/chromium/ipc/ipc_send_fds_test.cc @@ -11,6 +11,7 @@ extern "C" { } #endif #include <fcntl.h> +#include <stddef.h> #include <sys/socket.h> #include <sys/stat.h> #include <unistd.h> @@ -73,7 +74,7 @@ class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase { } void OnChannelError() override { - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } protected: @@ -96,7 +97,7 @@ class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase { ++num_fds_received_; if (num_fds_received_ == kNumFDsToSend * kNumMessages) - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } private: diff --git a/chromium/ipc/ipc_sync_channel.cc b/chromium/ipc/ipc_sync_channel.cc index 89dd7d7113e..d1942112d9a 100644 --- a/chromium/ipc/ipc_sync_channel.cc +++ b/chromium/ipc/ipc_sync_channel.cc @@ -4,6 +4,10 @@ #include "ipc/ipc_sync_channel.h" +#include <stddef.h> +#include <stdint.h> +#include <utility> + #include "base/bind.h" #include "base/lazy_instance.h" #include "base/location.h" @@ -305,15 +309,13 @@ bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) { return false; } - // TODO(bauerb): Remove logging once investigation of http://crbug.com/141055 - // has finished. if (!msg->is_reply_error()) { bool send_result = deserializers_.back().deserializer-> SerializeOutputParameters(*msg); deserializers_.back().send_result = send_result; - VLOG_IF(1, !send_result) << "Couldn't deserialize reply message"; + DVLOG_IF(1, !send_result) << "Couldn't deserialize reply message"; } else { - VLOG(1) << "Received error reply"; + DVLOG(1) << "Received error reply"; } deserializers_.back().done_event->Signal(); @@ -370,7 +372,7 @@ void SyncChannel::SyncContext::OnChannelClosed() { void SyncChannel::SyncContext::OnSendTimeout(int message_id) { base::AutoLock auto_lock(deserializers_lock_); PendingSyncMessageQueue::iterator iter; - VLOG(1) << "Send timeout"; + DVLOG(1) << "Send timeout"; for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) { if (iter->id == message_id) { iter->done_event->Signal(); @@ -382,8 +384,7 @@ void SyncChannel::SyncContext::OnSendTimeout(int message_id) { void SyncChannel::SyncContext::CancelPendingSends() { base::AutoLock auto_lock(deserializers_lock_); PendingSyncMessageQueue::iterator iter; - // TODO(bauerb): Remove once http://crbug/141055 is fixed. - VLOG(1) << "Canceling pending sends"; + DVLOG(1) << "Canceling pending sends"; for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) iter->done_event->Signal(); } @@ -416,7 +417,7 @@ scoped_ptr<SyncChannel> SyncChannel::Create( scoped_ptr<SyncChannel> channel = Create(listener, ipc_task_runner, shutdown_event); channel->Init(channel_handle, mode, create_pipe_now); - return channel.Pass(); + return channel; } // static @@ -428,8 +429,8 @@ scoped_ptr<SyncChannel> SyncChannel::Create( base::WaitableEvent* shutdown_event) { scoped_ptr<SyncChannel> channel = Create(listener, ipc_task_runner, shutdown_event); - channel->Init(factory.Pass(), create_pipe_now); - return channel.Pass(); + channel->Init(std::move(factory), create_pipe_now); + return channel; } // static @@ -487,7 +488,7 @@ bool SyncChannel::Send(Message* message) { // *this* might get deleted in WaitForReply. scoped_refptr<SyncContext> context(sync_context()); if (context->shutdown_event()->IsSignaled()) { - VLOG(1) << "shutdown event is signaled"; + DVLOG(1) << "shutdown event is signaled"; delete message; return false; } diff --git a/chromium/ipc/ipc_sync_channel_unittest.cc b/chromium/ipc/ipc_sync_channel_unittest.cc index a0968c7a051..21ebafb638c 100644 --- a/chromium/ipc/ipc_sync_channel_unittest.cc +++ b/chromium/ipc/ipc_sync_channel_unittest.cc @@ -4,6 +4,8 @@ #include "ipc/ipc_sync_channel.h" +#include <stddef.h> + #include <string> #include <vector> @@ -20,6 +22,7 @@ #include "base/thread_task_runner_handle.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" +#include "build/build_config.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_message.h" #include "ipc/ipc_sender.h" diff --git a/chromium/ipc/ipc_sync_message.cc b/chromium/ipc/ipc_sync_message.cc index 8a770b17a1f..884dd8029d5 100644 --- a/chromium/ipc/ipc_sync_message.cc +++ b/chromium/ipc/ipc_sync_message.cc @@ -4,6 +4,8 @@ #include "ipc/ipc_sync_message.h" +#include <stdint.h> + #include <stack> #include "base/atomic_sequence_num.h" diff --git a/chromium/ipc/ipc_sync_message.h b/chromium/ipc/ipc_sync_message.h index 904a9c8505c..6dd3b632451 100644 --- a/chromium/ipc/ipc_sync_message.h +++ b/chromium/ipc/ipc_sync_message.h @@ -13,6 +13,7 @@ #include <string> #include "base/memory/scoped_ptr.h" +#include "build/build_config.h" #include "ipc/ipc_message.h" namespace base { diff --git a/chromium/ipc/ipc_test_base.cc b/chromium/ipc/ipc_test_base.cc index f700856f244..6243138cd4b 100644 --- a/chromium/ipc/ipc_test_base.cc +++ b/chromium/ipc/ipc_test_base.cc @@ -2,15 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "build/build_config.h" - -#include "base/single_thread_task_runner.h" #include "ipc/ipc_test_base.h" +#include <utility> + #include "base/command_line.h" #include "base/process/kill.h" +#include "base/single_thread_task_runner.h" #include "base/threading/thread.h" #include "base/time/time.h" +#include "build/build_config.h" #include "ipc/ipc_descriptors.h" #if defined(OS_POSIX) @@ -48,7 +49,7 @@ void IPCTestBase::InitWithCustomMessageLoop( DCHECK(!message_loop_); test_client_name_ = test_client_name; - message_loop_ = message_loop.Pass(); + message_loop_ = std::move(message_loop); } void IPCTestBase::CreateChannel(IPC::Listener* listener) { @@ -61,11 +62,11 @@ bool IPCTestBase::ConnectChannel() { } scoped_ptr<IPC::Channel> IPCTestBase::ReleaseChannel() { - return channel_.Pass(); + return std::move(channel_); } void IPCTestBase::SetChannel(scoped_ptr<IPC::Channel> channel) { - channel_ = channel.Pass(); + channel_ = std::move(channel); } diff --git a/chromium/ipc/ipc_test_base.h b/chromium/ipc/ipc_test_base.h index 59f5b865403..360188f6a5b 100644 --- a/chromium/ipc/ipc_test_base.h +++ b/chromium/ipc/ipc_test_base.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/process/process.h" #include "base/test/multiprocess_test.h" +#include "build/build_config.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_factory.h" #include "ipc/ipc_channel_proxy.h" @@ -103,9 +104,6 @@ class IPCTestBase : public base::MultiProcessTest { IPC::Channel* channel() { return channel_.get(); } IPC::ChannelProxy* channel_proxy() { return channel_proxy_.get(); } - void set_attachment_broker(IPC::AttachmentBroker* broker) { - attachment_broker_ = broker; - } const base::Process& client_process() const { return client_process_; } scoped_refptr<base::SequencedTaskRunner> task_runner(); @@ -124,10 +122,6 @@ class IPCTestBase : public base::MultiProcessTest { scoped_ptr<IPC::Channel> channel_; scoped_ptr<IPC::ChannelProxy> channel_proxy_; - // The AttachmentBroker that should be passed to |channel_| when it is - // created. - IPC::AttachmentBroker* attachment_broker_; - base::Process client_process_; DISALLOW_COPY_AND_ASSIGN(IPCTestBase); diff --git a/chromium/ipc/ipc_test_channel_listener.cc b/chromium/ipc/ipc_test_channel_listener.cc index 7d1832dd43b..4d25ca3b903 100644 --- a/chromium/ipc/ipc_test_channel_listener.cc +++ b/chromium/ipc/ipc_test_channel_listener.cc @@ -49,12 +49,12 @@ bool TestChannelListener::OnMessageReceived(const IPC::Message& message) { void TestChannelListener::OnChannelError() { // There is a race when closing the channel so the last message may be lost. EXPECT_LE(messages_left_, 1); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } void TestChannelListener::SendNextMessage() { if (--messages_left_ <= 0) - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); else SendOneMessage(sender_, "Foo"); } diff --git a/chromium/ipc/ipc_test_messages.h b/chromium/ipc/ipc_test_messages.h index 4bf927eb054..a61d1b013d2 100644 --- a/chromium/ipc/ipc_test_messages.h +++ b/chromium/ipc/ipc_test_messages.h @@ -2,13 +2,37 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Multiply-included file, no traditional include guard. #include "build/build_config.h" -#if defined(OS_WIN) -#include "ipc/handle_win.h" #include "ipc/ipc_message_macros.h" #define IPC_MESSAGE_START IPCTestMsgStart +#if defined(OS_WIN) +#include "base/memory/shared_memory_handle.h" +#include "ipc/handle_win.h" + IPC_MESSAGE_CONTROL3(TestHandleWinMsg, int, IPC::HandleWin, int) IPC_MESSAGE_CONTROL2(TestTwoHandleWinMsg, IPC::HandleWin, IPC::HandleWin) +IPC_MESSAGE_CONTROL1(TestSharedMemoryHandleMsg1, base::SharedMemoryHandle) #endif // defined(OS_WIN) + +#if defined(OS_MACOSX) +#include "base/file_descriptor_posix.h" +#include "base/memory/shared_memory_handle.h" + +IPC_MESSAGE_CONTROL3(TestSharedMemoryHandleMsg1, + int, + base::SharedMemoryHandle, + int) +IPC_MESSAGE_CONTROL2(TestSharedMemoryHandleMsg2, + base::SharedMemoryHandle, + base::SharedMemoryHandle) +IPC_MESSAGE_CONTROL4(TestSharedMemoryHandleMsg3, + base::FileDescriptor, + base::SharedMemoryHandle, + base::FileDescriptor, + base::SharedMemoryHandle) +IPC_MESSAGE_CONTROL1(TestSharedMemoryHandleMsg4, int) + +#endif // defined(OS_MACOSX) diff --git a/chromium/ipc/ipc_test_sink.cc b/chromium/ipc/ipc_test_sink.cc index ab95a19a2ce..316609c3386 100644 --- a/chromium/ipc/ipc_test_sink.cc +++ b/chromium/ipc/ipc_test_sink.cc @@ -4,6 +4,10 @@ #include "ipc/ipc_test_sink.h" +#include <stddef.h> +#include <stdint.h> + +#include "build/build_config.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_message.h" diff --git a/chromium/ipc/ipc_test_sink.h b/chromium/ipc/ipc_test_sink.h index 5539c98d7cb..ab8531d5ea8 100644 --- a/chromium/ipc/ipc_test_sink.h +++ b/chromium/ipc/ipc_test_sink.h @@ -5,6 +5,7 @@ #ifndef IPC_IPC_TEST_SINK_H_ #define IPC_IPC_TEST_SINK_H_ +#include <stddef.h> #include <stdint.h> #include <utility> @@ -13,6 +14,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "base/observer_list.h" +#include "build/build_config.h" #include "ipc/ipc_channel.h" namespace IPC { @@ -59,7 +61,7 @@ class Message; // public: // virtual bool OnMessageReceived(const IPC::Message& msg) { // <do something with the message> -// MessageLoop::current()->Quit(); +// MessageLoop::current()->QuitWhenIdle(); // return false; // to store the message in the sink, or true to drop it // } // }; diff --git a/chromium/ipc/ipc_tests_apk.isolate b/chromium/ipc/ipc_tests_apk.isolate new file mode 100644 index 00000000000..0c0cf1c5ca7 --- /dev/null +++ b/chromium/ipc/ipc_tests_apk.isolate @@ -0,0 +1,17 @@ +# 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. +{ + 'includes': [ + '../build/android/android.isolate', + ], + 'variables': { + 'command': [ + '<(PRODUCT_DIR)/bin/run_ipc_tests', + ], + 'files': [ + '<(PRODUCT_DIR)/bin/run_ipc_tests', + '<(PRODUCT_DIR)/ipc_tests_apk/', + ] + }, +} diff --git a/chromium/ipc/mach_port_attachment_mac.cc b/chromium/ipc/mach_port_attachment_mac.cc index 2a0cfa44bc8..65baa3480ed 100644 --- a/chromium/ipc/mach_port_attachment_mac.cc +++ b/chromium/ipc/mach_port_attachment_mac.cc @@ -4,21 +4,36 @@ #include "ipc/mach_port_attachment_mac.h" +#include <stdint.h> + +#include "base/mac/mach_logging.h" + namespace IPC { namespace internal { MachPortAttachmentMac::MachPortAttachmentMac(mach_port_t mach_port) - : mach_port_(mach_port) {} + : mach_port_(mach_port), owns_mach_port_(true) { + if (mach_port != MACH_PORT_NULL) { + kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_port, + MACH_PORT_RIGHT_SEND, 1); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) + << "MachPortAttachmentMac mach_port_mod_refs"; + } +} MachPortAttachmentMac::MachPortAttachmentMac(const WireFormat& wire_format) : BrokerableAttachment(wire_format.attachment_id), - mach_port_(static_cast<mach_port_t>(wire_format.mach_port)) {} - -MachPortAttachmentMac::MachPortAttachmentMac( - const BrokerableAttachment::AttachmentId& id) - : BrokerableAttachment(id), mach_port_(MACH_PORT_NULL) {} - -MachPortAttachmentMac::~MachPortAttachmentMac() {} + mach_port_(static_cast<mach_port_t>(wire_format.mach_port)), + owns_mach_port_(true) {} + +MachPortAttachmentMac::~MachPortAttachmentMac() { + if (mach_port_ != MACH_PORT_NULL && owns_mach_port_) { + kern_return_t kr = mach_port_mod_refs(mach_task_self(), mach_port_, + MACH_PORT_RIGHT_SEND, -1); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) + << "~MachPortAttachmentMac mach_port_mod_refs"; + } +} MachPortAttachmentMac::BrokerableType MachPortAttachmentMac::GetBrokerableType() const { diff --git a/chromium/ipc/mach_port_attachment_mac.h b/chromium/ipc/mach_port_attachment_mac.h index efe93c46a13..244014ed724 100644 --- a/chromium/ipc/mach_port_attachment_mac.h +++ b/chromium/ipc/mach_port_attachment_mac.h @@ -8,6 +8,7 @@ #include <mach/mach.h> #include <stdint.h> +#include "base/macros.h" #include "base/process/process_handle.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_export.h" @@ -45,9 +46,15 @@ class IPC_EXPORT MachPortAttachmentMac : public BrokerableAttachment { AttachmentId attachment_id; }; + // This constructor increments the ref count of |mach_port_| and takes + // ownership of the result. Should only be called by the sender of a Chrome + // IPC message. explicit MachPortAttachmentMac(mach_port_t mach_port); + + // This constructor takes ownership of |wire_format.mach_port|, but does not + // modify its ref count. Should only be called by the receiver of a Chrome IPC + // message. explicit MachPortAttachmentMac(const WireFormat& wire_format); - explicit MachPortAttachmentMac(const BrokerableAttachment::AttachmentId& id); BrokerableType GetBrokerableType() const override; @@ -56,9 +63,20 @@ class IPC_EXPORT MachPortAttachmentMac : public BrokerableAttachment { mach_port_t get_mach_port() const { return mach_port_; } + // The caller of this method has taken ownership of |mach_port_|. + void reset_mach_port_ownership() { owns_mach_port_ = false; } + private: ~MachPortAttachmentMac() override; - mach_port_t mach_port_; + const mach_port_t mach_port_; + + // In the sender process, the attachment owns the Mach port of a newly created + // message. The attachment broker will eventually take ownership of + // |mach_port_|. + // In the destination process, the attachment owns |mach_port_| until + // ParamTraits<MachPortMac>::Read() is called, which takes ownership. + bool owns_mach_port_; + DISALLOW_COPY_AND_ASSIGN(MachPortAttachmentMac); }; } // namespace internal diff --git a/chromium/ipc/mach_port_mac.cc b/chromium/ipc/mach_port_mac.cc index 96f4acaeb21..51a5bd7abfc 100644 --- a/chromium/ipc/mach_port_mac.cc +++ b/chromium/ipc/mach_port_mac.cc @@ -37,6 +37,7 @@ bool ParamTraits<MachPortMac>::Read(const Message* m, IPC::internal::MachPortAttachmentMac* mach_port_attachment = static_cast<IPC::internal::MachPortAttachmentMac*>(brokerable_attachment); r->set_mach_port(mach_port_attachment->get_mach_port()); + mach_port_attachment->reset_mach_port_ownership(); return true; } diff --git a/chromium/ipc/mach_port_mac.h b/chromium/ipc/mach_port_mac.h index 0193f9dadd9..5c420c0fed3 100644 --- a/chromium/ipc/mach_port_mac.h +++ b/chromium/ipc/mach_port_mac.h @@ -7,24 +7,65 @@ #include <mach/mach.h> +#include "base/macros.h" #include "ipc/ipc_export.h" #include "ipc/ipc_message_macros.h" namespace IPC { -// MachPortMac is a wrapper around an OSX mach_port_t that can be transported -// across Chrome IPC channels that support attachment brokering. The mach_port_t -// will be duplicated into the destination process by the attachment broker. +// MachPortMac is a wrapper around an OSX Mach port that can be transported +// across Chrome IPC channels that support attachment brokering. The send right +// to the Mach port will be duplicated into the destination process by the +// attachment broker. If needed, attachment brokering can be trivially extended +// to support duplication of other types of rights. class IPC_EXPORT MachPortMac { public: MachPortMac() : mach_port_(MACH_PORT_NULL) {} - explicit MachPortMac(const mach_port_t& mach_port) : mach_port_(mach_port) {} + + explicit MachPortMac(mach_port_t mach_port) : mach_port_(mach_port) {} mach_port_t get_mach_port() const { return mach_port_; } + + // This method should only be used by ipc/ translation code. void set_mach_port(mach_port_t mach_port) { mach_port_ = mach_port; } private: + // The ownership semantics of |mach_port_| cannot be easily expressed with a + // C++ scoped object. This is partly due to the mechanism by which Mach ports + // are brokered, and partly due to the architecture of Chrome IPC. + // + // The broker for Mach ports may live in a different process than the sender + // of the original Chrome IPC. In this case, it is signalled asynchronously, + // and ownership of the Mach port passes from the sender process into the + // broker process. + // + // Chrome IPC is written with the assumption that translation is a stateless + // process. There is no good mechanism to convey the semantics of ownership + // transfer from the Chrome IPC stack into the client code that receives the + // translated message. As a result, Chrome IPC code assumes that every message + // has a handler, and that the handler will take ownership of the Mach port. + // Note that the same holds true for POSIX fds and Windows HANDLEs. + // + // When used by client code in the sender process, this class is just a + // wrapper. The client code calls Send(new Message(MachPortMac(mach_port))) + // and continues on its merry way. Behind the scenes, a MachPortAttachmentMac + // takes ownership of the Mach port. When the attachment broker sends the name + // of the Mach port to the broker process, it also releases + // MachPortAttachmentMac's reference to the Mach port, as ownership has + // effectively been transferred to the broker process. + // + // The broker process receives the name, duplicates the Mach port into the + // destination process, and then decrements the ref count in the original + // process. + // + // In the destination process, the attachment broker is responsible for + // coupling the Mach port (inserted by the broker process) with Chrome IPC in + // the form of a MachPortAttachmentMac. Due to the Chrome IPC translation + // semantics discussed above, this MachPortAttachmentMac does not take + // ownership of the Mach port, and assumes that the client code which receives + // the callback will take ownership of the Mach port. mach_port_t mach_port_; + DISALLOW_COPY_AND_ASSIGN(MachPortMac); }; template <> diff --git a/chromium/ipc/message_filter.cc b/chromium/ipc/message_filter.cc index b9436e234ac..dd5ec30fe41 100644 --- a/chromium/ipc/message_filter.cc +++ b/chromium/ipc/message_filter.cc @@ -4,6 +4,8 @@ #include "ipc/message_filter.h" +#include <stdint.h> + #include "base/memory/ref_counted.h" #include "ipc/ipc_channel.h" diff --git a/chromium/ipc/message_filter_router.cc b/chromium/ipc/message_filter_router.cc index 35209b09181..56075d68891 100644 --- a/chromium/ipc/message_filter_router.cc +++ b/chromium/ipc/message_filter_router.cc @@ -4,8 +4,10 @@ #include "ipc/message_filter_router.h" +#include <stddef.h> #include <stdint.h> +#include "base/macros.h" #include "ipc/ipc_message_macros.h" #include "ipc/ipc_message_utils.h" #include "ipc/message_filter.h" diff --git a/chromium/ipc/mojo/BUILD.gn b/chromium/ipc/mojo/BUILD.gn index d801a7b9c19..29e3a957420 100644 --- a/chromium/ipc/mojo/BUILD.gn +++ b/chromium/ipc/mojo/BUILD.gn @@ -2,8 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//mojo/public/tools/bindings/mojom.gni") import("//testing/test.gni") -import("//third_party/mojo/src/mojo/public/tools/bindings/mojom.gni") mojom("client_channel") { sources = [ @@ -35,14 +35,14 @@ component("mojo") { defines = [ "IPC_MOJO_IMPLEMENTATION" ] deps = [ + ":client_channel", "//base", "//base/third_party/dynamic_annotations", "//ipc", "//mojo/environment:chromium", + "//mojo/public/c/environment:environment", + "//mojo/public/cpp/bindings", "//third_party/mojo/src/mojo/edk/system", - "//third_party/mojo/src/mojo/public/c/environment:environment", - "//third_party/mojo/src/mojo/public/cpp/bindings", - ":client_channel", ] } diff --git a/chromium/ipc/mojo/DEPS b/chromium/ipc/mojo/DEPS index 40dca36e2df..59e80a9bf82 100644 --- a/chromium/ipc/mojo/DEPS +++ b/chromium/ipc/mojo/DEPS @@ -1,5 +1,5 @@ include_rules = [ "+mojo/edk/embedder", - "+third_party/mojo/src/mojo/public", + "+mojo/public", "+third_party/mojo/src/mojo/edk/embedder", ] diff --git a/chromium/ipc/mojo/OWNERS b/chromium/ipc/mojo/OWNERS index 584a1f68941..ae47a47a0f7 100644 --- a/chromium/ipc/mojo/OWNERS +++ b/chromium/ipc/mojo/OWNERS @@ -1,2 +1,4 @@ +amistry@chromium.org morrita@chromium.org -viettrungluu@chromium.org
\ No newline at end of file +rockot@chromium.org +viettrungluu@chromium.org diff --git a/chromium/ipc/mojo/async_handle_waiter.cc b/chromium/ipc/mojo/async_handle_waiter.cc index 652764edc6c..4e07480f6cf 100644 --- a/chromium/ipc/mojo/async_handle_waiter.cc +++ b/chromium/ipc/mojo/async_handle_waiter.cc @@ -9,6 +9,7 @@ #include "base/bind_helpers.h" #include "base/location.h" #include "base/logging.h" +#include "base/macros.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" namespace IPC { diff --git a/chromium/ipc/mojo/async_handle_waiter.h b/chromium/ipc/mojo/async_handle_waiter.h index d6cc74510b2..e82c27ae29c 100644 --- a/chromium/ipc/mojo/async_handle_waiter.h +++ b/chromium/ipc/mojo/async_handle_waiter.h @@ -6,11 +6,12 @@ #define IPC_MOJO_ASYNC_HANDLE_WAITER_H_ #include "base/callback.h" +#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "ipc/ipc_export.h" -#include "third_party/mojo/src/mojo/public/c/system/types.h" +#include "mojo/public/c/system/types.h" namespace IPC { namespace internal { diff --git a/chromium/ipc/mojo/async_handle_waiter_unittest.cc b/chromium/ipc/mojo/async_handle_waiter_unittest.cc index 46d4c7ef739..e17b4fd3b09 100644 --- a/chromium/ipc/mojo/async_handle_waiter_unittest.cc +++ b/chromium/ipc/mojo/async_handle_waiter_unittest.cc @@ -4,13 +4,16 @@ #include "ipc/mojo/async_handle_waiter.h" +#include <stddef.h> +#include <stdint.h> + #include "base/bind.h" #include "base/location.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread.h" +#include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" namespace IPC { namespace internal { diff --git a/chromium/ipc/mojo/ipc_channel_mojo.cc b/chromium/ipc/mojo/ipc_channel_mojo.cc index 3f4d31f7230..404c814516e 100644 --- a/chromium/ipc/mojo/ipc_channel_mojo.cc +++ b/chromium/ipc/mojo/ipc_channel_mojo.cc @@ -4,11 +4,18 @@ #include "ipc/mojo/ipc_channel_mojo.h" +#include <stddef.h> +#include <stdint.h> +#include <memory> +#include <utility> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/command_line.h" #include "base/lazy_instance.h" +#include "base/macros.h" #include "base/thread_task_runner_handle.h" +#include "build/build_config.h" #include "ipc/ipc_listener.h" #include "ipc/ipc_logging.h" #include "ipc/ipc_message_attachment_set.h" @@ -16,9 +23,8 @@ #include "ipc/mojo/client_channel.mojom.h" #include "ipc/mojo/ipc_mojo_bootstrap.h" #include "ipc/mojo/ipc_mojo_handle_attachment.h" -#include "mojo/edk/embedder/embedder.h" +#include "mojo/public/cpp/bindings/binding.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" -#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" #if defined(OS_POSIX) && !defined(OS_NACL) #include "ipc/ipc_platform_file_attachment_posix.h" @@ -68,20 +74,19 @@ class ClientChannelMojo : public ChannelMojo, public ClientChannel { // MojoBootstrap::Delegate implementation void OnPipeAvailable(mojo::embedder::ScopedPlatformHandle handle, - int32 peer_pid) override { + int32_t peer_pid) override { if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { - mojo::edk::ScopedPlatformHandle edk_handle(mojo::edk::PlatformHandle( -#if defined(OS_WIN) - handle.release().handle)); -#else - handle.release().fd)); -#endif InitMessageReader( - mojo::edk::CreateMessagePipe(edk_handle.Pass()), peer_pid); + mojo::embedder::CreateChannel( + std::move(handle), + base::Callback<void(mojo::embedder::ChannelInfo*)>(), + scoped_refptr<base::TaskRunner>()), + peer_pid); return; } - CreateMessagingPipe(handle.Pass(), base::Bind(&ClientChannelMojo::BindPipe, - weak_factory_.GetWeakPtr())); + CreateMessagingPipe( + std::move(handle), + base::Bind(&ClientChannelMojo::BindPipe, weak_factory_.GetWeakPtr())); } // ClientChannel implementation @@ -89,13 +94,13 @@ class ClientChannelMojo : public ChannelMojo, public ClientChannel { mojo::ScopedMessagePipeHandle pipe, int32_t peer_pid, const mojo::Callback<void(int32_t)>& callback) override { - InitMessageReader(pipe.Pass(), static_cast<base::ProcessId>(peer_pid)); + InitMessageReader(std::move(pipe), static_cast<base::ProcessId>(peer_pid)); callback.Run(GetSelfPID()); } private: void BindPipe(mojo::ScopedMessagePipeHandle handle) { - binding_.Bind(handle.Pass()); + binding_.Bind(std::move(handle)); } void OnConnectionError() { listener()->OnChannelError(); @@ -123,21 +128,18 @@ class ServerChannelMojo : public ChannelMojo { // MojoBootstrap::Delegate implementation void OnPipeAvailable(mojo::embedder::ScopedPlatformHandle handle, - int32 peer_pid) override { + int32_t peer_pid) override { if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { - mojo::edk::ScopedPlatformHandle edk_handle(mojo::edk::PlatformHandle( -#if defined(OS_WIN) - handle.release().handle)); -#else - handle.release().fd)); -#endif - message_pipe_ = mojo::edk::CreateMessagePipe(edk_handle.Pass()); + message_pipe_ = mojo::embedder::CreateChannel( + std::move(handle), + base::Callback<void(mojo::embedder::ChannelInfo*)>(), + scoped_refptr<base::TaskRunner>()); if (!message_pipe_.is_valid()) { LOG(WARNING) << "mojo::CreateMessagePipe failed: "; listener()->OnChannelError(); return; } - InitMessageReader(message_pipe_.Pass(), peer_pid); + InitMessageReader(std::move(message_pipe_), peer_pid); return; } @@ -150,7 +152,7 @@ class ServerChannelMojo : public ChannelMojo { return; } CreateMessagingPipe( - handle.Pass(), + std::move(handle), base::Bind(&ServerChannelMojo::InitClientChannel, weak_factory_.GetWeakPtr(), base::Passed(&peer))); } @@ -165,11 +167,11 @@ class ServerChannelMojo : public ChannelMojo { void InitClientChannel(mojo::ScopedMessagePipeHandle peer_handle, mojo::ScopedMessagePipeHandle handle) { client_channel_.Bind( - mojo::InterfacePtrInfo<ClientChannel>(handle.Pass(), 0u)); + mojo::InterfacePtrInfo<ClientChannel>(std::move(handle), 0u)); client_channel_.set_connection_error_handler(base::Bind( &ServerChannelMojo::OnConnectionError, base::Unretained(this))); client_channel_->Init( - peer_handle.Pass(), static_cast<int32_t>(GetSelfPID()), + std::move(peer_handle), static_cast<int32_t>(GetSelfPID()), base::Bind(&ServerChannelMojo::ClientChannelWasInitialized, base::Unretained(this))); } @@ -180,7 +182,7 @@ class ServerChannelMojo : public ChannelMojo { // ClientChannelClient implementation void ClientChannelWasInitialized(int32_t peer_pid) { - InitMessageReader(message_pipe_.Pass(), peer_pid); + InitMessageReader(std::move(message_pipe_), peer_pid); } mojo::InterfacePtr<ClientChannel> client_channel_; @@ -303,8 +305,9 @@ void ChannelMojo::CreateMessagingPipe( weak_factory_.GetWeakPtr(), callback); if (!g_use_channel_on_io_thread_only || base::ThreadTaskRunnerHandle::Get() == io_runner_) { - CreateMessagingPipeOnIOThread( - handle.Pass(), base::ThreadTaskRunnerHandle::Get(), return_callback); + CreateMessagingPipeOnIOThread(std::move(handle), + base::ThreadTaskRunnerHandle::Get(), + return_callback); } else { io_runner_->PostTask( FROM_HERE, @@ -321,9 +324,9 @@ void ChannelMojo::CreateMessagingPipeOnIOThread( const CreateMessagingPipeOnIOThreadCallback& callback) { mojo::embedder::ChannelInfo* channel_info; mojo::ScopedMessagePipeHandle pipe = - mojo::embedder::CreateChannelOnIOThread(handle.Pass(), &channel_info); + mojo::embedder::CreateChannelOnIOThread(std::move(handle), &channel_info); if (base::ThreadTaskRunnerHandle::Get() == callback_runner) { - callback.Run(pipe.Pass(), channel_info); + callback.Run(std::move(pipe), channel_info); } else { callback_runner->PostTask( FROM_HERE, base::Bind(callback, base::Passed(&pipe), channel_info)); @@ -337,7 +340,7 @@ void ChannelMojo::OnMessagingPipeCreated( DCHECK(!channel_info_.get()); channel_info_ = scoped_ptr<mojo::embedder::ChannelInfo, ChannelInfoDeleter>( channel_info, ChannelInfoDeleter(io_runner_)); - callback.Run(handle.Pass()); + callback.Run(std::move(handle)); } bool ChannelMojo::Connect() { @@ -352,7 +355,7 @@ void ChannelMojo::Close() { // |message_reader_| has to be cleared inside the lock, // but the instance has to be deleted outside. base::AutoLock l(lock_); - to_be_deleted = message_reader_.Pass(); + to_be_deleted = std::move(message_reader_); // We might Close() before we Connect(). waiting_connect_ = false; } @@ -371,7 +374,7 @@ namespace { // ClosingDeleter calls |CloseWithErrorIfPending| before deleting the // |MessagePipeReader|. struct ClosingDeleter { - typedef base::DefaultDeleter<internal::MessagePipeReader> DefaultType; + typedef std::default_delete<internal::MessagePipeReader> DefaultType; void operator()(internal::MessagePipeReader* ptr) const { ptr->CloseWithErrorIfPending(); @@ -384,7 +387,7 @@ struct ClosingDeleter { void ChannelMojo::InitMessageReader(mojo::ScopedMessagePipeHandle pipe, int32_t peer_pid) { scoped_ptr<internal::MessagePipeReader, ClosingDeleter> reader( - new internal::MessagePipeReader(pipe.Pass(), this)); + new internal::MessagePipeReader(std::move(pipe), this)); { base::AutoLock l(lock_); @@ -475,8 +478,9 @@ MojoResult ChannelMojo::ReadFromMessageAttachmentSet( // of FDs, so just to dup()-and-own them is the safest option. if (message->HasAttachments()) { MessageAttachmentSet* set = message->attachment_set(); - for (unsigned i = 0; i < set->size(); ++i) { - scoped_refptr<MessageAttachment> attachment = set->GetAttachmentAt(i); + for (unsigned i = 0; i < set->num_non_brokerable_attachments(); ++i) { + scoped_refptr<MessageAttachment> attachment = + set->GetNonBrokerableAttachmentAt(i); switch (attachment->GetType()) { case MessageAttachment::TYPE_PLATFORM_FILE: #if defined(OS_POSIX) && !defined(OS_NACL) @@ -486,7 +490,7 @@ MojoResult ChannelMojo::ReadFromMessageAttachmentSet( attachment.get())); if (!file.is_valid()) { DPLOG(WARNING) << "Failed to dup FD to transmit."; - set->CommitAll(); + set->CommitAllDescriptors(); return MOJO_RESULT_UNKNOWN; } @@ -498,7 +502,7 @@ MojoResult ChannelMojo::ReadFromMessageAttachmentSet( if (MOJO_RESULT_OK != wrap_result) { LOG(WARNING) << "Pipe failed to wrap handles. Closing: " << wrap_result; - set->CommitAll(); + set->CommitAllDescriptors(); return wrap_result; } @@ -522,7 +526,7 @@ MojoResult ChannelMojo::ReadFromMessageAttachmentSet( } } - set->CommitAll(); + set->CommitAllDescriptors(); } return MOJO_RESULT_OK; diff --git a/chromium/ipc/mojo/ipc_channel_mojo.h b/chromium/ipc/mojo/ipc_channel_mojo.h index 834fd420da2..a5ddf1e4077 100644 --- a/chromium/ipc/mojo/ipc_channel_mojo.h +++ b/chromium/ipc/mojo/ipc_channel_mojo.h @@ -5,20 +5,24 @@ #ifndef IPC_IPC_CHANNEL_MOJO_H_ #define IPC_IPC_CHANNEL_MOJO_H_ +#include <stdint.h> + #include <vector> +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "base/synchronization/lock.h" +#include "build/build_config.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_factory.h" #include "ipc/ipc_export.h" #include "ipc/mojo/ipc_message_pipe_reader.h" #include "ipc/mojo/ipc_mojo_bootstrap.h" #include "ipc/mojo/scoped_ipc_support.h" +#include "mojo/public/cpp/system/core.h" #include "third_party/mojo/src/mojo/edk/embedder/channel_info_forward.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" namespace IPC { diff --git a/chromium/ipc/mojo/ipc_channel_mojo_unittest.cc b/chromium/ipc/mojo/ipc_channel_mojo_unittest.cc index 58d3e9f7e98..b4f4154c67c 100644 --- a/chromium/ipc/mojo/ipc_channel_mojo_unittest.cc +++ b/chromium/ipc/mojo/ipc_channel_mojo_unittest.cc @@ -4,7 +4,9 @@ #include "ipc/mojo/ipc_channel_mojo.h" +#include <stddef.h> #include <stdint.h> +#include <utility> #include "base/base_paths.h" #include "base/files/file.h" @@ -16,6 +18,7 @@ #include "base/test/test_timeouts.h" #include "base/thread_task_runner_handle.h" #include "base/threading/thread.h" +#include "build/build_config.h" #include "ipc/ipc_message.h" #include "ipc/ipc_test_base.h" #include "ipc/ipc_test_channel_listener.h" @@ -44,7 +47,7 @@ class ListenerThatExpectsOK : public IPC::Listener { EXPECT_TRUE(iter.ReadString(&should_be_ok)); EXPECT_EQ(should_be_ok, "OK"); received_ok_ = true; - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); return true; } @@ -202,14 +205,14 @@ class ListenerExpectingErrors : public IPC::Listener { } void OnChannelConnected(int32_t peer_pid) override { - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } bool OnMessageReceived(const IPC::Message& message) override { return true; } void OnChannelError() override { has_error_ = true; - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } bool has_error() const { return has_error_; } @@ -244,7 +247,7 @@ class ListenerThatQuits : public IPC::Listener { } void OnChannelConnected(int32_t peer_pid) override { - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } }; @@ -314,8 +317,8 @@ class HandleSendingHelper { mojo::WriteMessageRaw(pipe->self.get(), &content[0], static_cast<uint32_t>(content.size()), nullptr, 0, 0)); - EXPECT_TRUE( - IPC::MojoMessageHelper::WriteMessagePipeTo(message, pipe->peer.Pass())); + EXPECT_TRUE(IPC::MojoMessageHelper::WriteMessagePipeTo( + message, std::move(pipe->peer))); } static void WritePipeThenSend(IPC::Sender* sender, TestingMessagePipe* pipe) { @@ -394,7 +397,7 @@ class ListenerThatExpectsMessagePipe : public IPC::Listener { bool OnMessageReceived(const IPC::Message& message) override { base::PickleIterator iter(message); HandleSendingHelper::ReadReceivedPipe(message, &iter); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); ListenerThatExpectsOK::SendOK(sender_); return true; } @@ -478,7 +481,7 @@ class ListenerThatExpectsMessagePipeUsingParamTrait : public IPC::Listener { MojoClose(handle.value()); } - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); ListenerThatExpectsOK::SendOK(sender_); return true; } @@ -597,7 +600,7 @@ class ListenerSendingOneOk : public IPC::Listener { void OnChannelConnected(int32_t peer_pid) override { ListenerThatExpectsOK::SendOK(sender_); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } void set_sender(IPC::Sender* sender) { sender_ = sender; } @@ -686,7 +689,7 @@ class ListenerThatExpectsFile : public IPC::Listener { bool OnMessageReceived(const IPC::Message& message) override { base::PickleIterator iter(message); HandleSendingHelper::ReadReceivedFile(message, &iter); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); ListenerThatExpectsOK::SendOK(sender_); return true; } @@ -751,7 +754,7 @@ class ListenerThatExpectsFileAndPipe : public IPC::Listener { base::PickleIterator iter(message); HandleSendingHelper::ReadReceivedFile(message, &iter); HandleSendingHelper::ReadReceivedPipe(message, &iter); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); ListenerThatExpectsOK::SendOK(sender_); return true; } @@ -816,7 +819,7 @@ class ListenerThatVerifiesPeerPid : public IPC::Listener { public: void OnChannelConnected(int32_t peer_pid) override { EXPECT_EQ(peer_pid, kMagicChildId); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } bool OnMessageReceived(const IPC::Message& message) override { diff --git a/chromium/ipc/mojo/ipc_message_pipe_reader.cc b/chromium/ipc/mojo/ipc_message_pipe_reader.cc index 11aab8d6155..19d9e303a6d 100644 --- a/chromium/ipc/mojo/ipc_message_pipe_reader.cc +++ b/chromium/ipc/mojo/ipc_message_pipe_reader.cc @@ -5,6 +5,7 @@ #include "ipc/mojo/ipc_message_pipe_reader.h" #include <stdint.h> +#include <utility> #include "base/bind.h" #include "base/bind_helpers.h" @@ -20,14 +21,12 @@ namespace internal { MessagePipeReader::MessagePipeReader(mojo::ScopedMessagePipeHandle handle, MessagePipeReader::Delegate* delegate) - : pipe_(handle.Pass()), + : pipe_(std::move(handle)), handle_copy_(pipe_.get().value()), delegate_(delegate), - async_waiter_( - new AsyncHandleWaiter(base::Bind(&MessagePipeReader::PipeIsReady, - base::Unretained(this)))), - pending_send_error_(MOJO_RESULT_OK) { -} + async_waiter_(new AsyncHandleWaiter( + base::Bind(&MessagePipeReader::PipeIsReady, base::Unretained(this)))), + pending_send_error_(MOJO_RESULT_OK) {} MessagePipeReader::~MessagePipeReader() { DCHECK(thread_checker_.CalledOnValidThread()); diff --git a/chromium/ipc/mojo/ipc_message_pipe_reader.h b/chromium/ipc/mojo/ipc_message_pipe_reader.h index b9c11c60372..375812305ca 100644 --- a/chromium/ipc/mojo/ipc_message_pipe_reader.h +++ b/chromium/ipc/mojo/ipc_message_pipe_reader.h @@ -5,15 +5,19 @@ #ifndef IPC_IPC_MESSAGE_PIPE_READER_H_ #define IPC_IPC_MESSAGE_PIPE_READER_H_ +#include <stdint.h> + +#include <memory> #include <vector> #include "base/atomicops.h" #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/threading/thread_checker.h" #include "ipc/ipc_message.h" -#include "third_party/mojo/src/mojo/public/c/environment/async_waiter.h" -#include "third_party/mojo/src/mojo/public/cpp/system/core.h" +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/system/core.h" namespace IPC { namespace internal { @@ -49,7 +53,7 @@ class MessagePipeReader { // This is intended to used by MessagePipeReader owners. class DelayedDeleter { public: - typedef base::DefaultDeleter<MessagePipeReader> DefaultType; + typedef std::default_delete<MessagePipeReader> DefaultType; static void DeleteNow(MessagePipeReader* ptr) { delete ptr; } diff --git a/chromium/ipc/mojo/ipc_mojo_bootstrap.cc b/chromium/ipc/mojo/ipc_mojo_bootstrap.cc index b668a7bcac2..d2966756605 100644 --- a/chromium/ipc/mojo/ipc_mojo_bootstrap.cc +++ b/chromium/ipc/mojo/ipc_mojo_bootstrap.cc @@ -5,9 +5,12 @@ #include "ipc/mojo/ipc_mojo_bootstrap.h" #include <stdint.h> +#include <utility> #include "base/logging.h" +#include "base/macros.h" #include "base/process/process_handle.h" +#include "build/build_config.h" #include "ipc/ipc_message_utils.h" #include "ipc/ipc_platform_file.h" #include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h" @@ -109,7 +112,7 @@ class MojoClientBootstrap : public MojoBootstrap { bool OnMessageReceived(const Message& message) override; void OnChannelConnected(int32_t peer_pid) override; - int32 peer_pid_; + int32_t peer_pid_; DISALLOW_COPY_AND_ASSIGN(MojoClientBootstrap); }; @@ -162,8 +165,8 @@ scoped_ptr<MojoBootstrap> MojoBootstrap::Create(ChannelHandle handle, scoped_ptr<Channel> bootstrap_channel = Channel::Create(handle, mode, self.get()); - self->Init(bootstrap_channel.Pass(), delegate); - return self.Pass(); + self->Init(std::move(bootstrap_channel), delegate); + return self; } MojoBootstrap::MojoBootstrap() : delegate_(NULL), state_(STATE_INITIALIZED) { @@ -173,7 +176,7 @@ MojoBootstrap::~MojoBootstrap() { } void MojoBootstrap::Init(scoped_ptr<Channel> channel, Delegate* delegate) { - channel_ = channel.Pass(); + channel_ = std::move(channel); delegate_ = delegate; } diff --git a/chromium/ipc/mojo/ipc_mojo_bootstrap.h b/chromium/ipc/mojo/ipc_mojo_bootstrap.h index 27a038baa5e..4b5ccfb71e9 100644 --- a/chromium/ipc/mojo/ipc_mojo_bootstrap.h +++ b/chromium/ipc/mojo/ipc_mojo_bootstrap.h @@ -5,8 +5,12 @@ #ifndef IPC_MOJO_IPC_MOJO_BOOTSTRAP_H_ #define IPC_MOJO_IPC_MOJO_BOOTSTRAP_H_ +#include <stdint.h> + +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/process/process_handle.h" +#include "build/build_config.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_listener.h" #include "third_party/mojo/src/mojo/edk/embedder/scoped_platform_handle.h" @@ -27,9 +31,8 @@ class IPC_MOJO_EXPORT MojoBootstrap : public Listener { public: class Delegate { public: - virtual void OnPipeAvailable( - mojo::embedder::ScopedPlatformHandle handle, - int32 peer_pid) = 0; + virtual void OnPipeAvailable(mojo::embedder::ScopedPlatformHandle handle, + int32_t peer_pid) = 0; virtual void OnBootstrapError() = 0; }; diff --git a/chromium/ipc/mojo/ipc_mojo_bootstrap_unittest.cc b/chromium/ipc/mojo/ipc_mojo_bootstrap_unittest.cc index 528c2350332..60046d16c52 100644 --- a/chromium/ipc/mojo/ipc_mojo_bootstrap_unittest.cc +++ b/chromium/ipc/mojo/ipc_mojo_bootstrap_unittest.cc @@ -4,9 +4,12 @@ #include "ipc/mojo/ipc_mojo_bootstrap.h" +#include <stdint.h> + #include "base/base_paths.h" #include "base/files/file.h" #include "base/message_loop/message_loop.h" +#include "build/build_config.h" #include "ipc/ipc_test_base.h" #if defined(OS_POSIX) @@ -24,7 +27,7 @@ class TestingDelegate : public IPC::MojoBootstrap::Delegate { TestingDelegate() : passed_(false) {} void OnPipeAvailable(mojo::embedder::ScopedPlatformHandle handle, - int32 peer_pid) override; + int32_t peer_pid) override; void OnBootstrapError() override; bool passed() const { return passed_; } @@ -35,13 +38,13 @@ class TestingDelegate : public IPC::MojoBootstrap::Delegate { void TestingDelegate::OnPipeAvailable( mojo::embedder::ScopedPlatformHandle handle, - int32 peer_pid) { + int32_t peer_pid) { passed_ = true; - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } void TestingDelegate::OnBootstrapError() { - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } // Times out on Android; see http://crbug.com/502290 diff --git a/chromium/ipc/mojo/ipc_mojo_handle_attachment.cc b/chromium/ipc/mojo/ipc_mojo_handle_attachment.cc index 9aae281c4ec..70b80c57b13 100644 --- a/chromium/ipc/mojo/ipc_mojo_handle_attachment.cc +++ b/chromium/ipc/mojo/ipc_mojo_handle_attachment.cc @@ -4,6 +4,9 @@ #include "ipc/mojo/ipc_mojo_handle_attachment.h" +#include <utility> + +#include "build/build_config.h" #include "ipc/ipc_message_attachment_set.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" @@ -11,8 +14,7 @@ namespace IPC { namespace internal { MojoHandleAttachment::MojoHandleAttachment(mojo::ScopedHandle handle) - : handle_(handle.Pass()) { -} + : handle_(std::move(handle)) {} MojoHandleAttachment::~MojoHandleAttachment() { } @@ -37,7 +39,7 @@ base::PlatformFile MojoHandleAttachment::TakePlatformFile() { #endif // OS_POSIX mojo::ScopedHandle MojoHandleAttachment::TakeHandle() { - return handle_.Pass(); + return std::move(handle_); } } // namespace internal diff --git a/chromium/ipc/mojo/ipc_mojo_handle_attachment.h b/chromium/ipc/mojo/ipc_mojo_handle_attachment.h index ef5a318ec72..2d51879c7af 100644 --- a/chromium/ipc/mojo/ipc_mojo_handle_attachment.h +++ b/chromium/ipc/mojo/ipc_mojo_handle_attachment.h @@ -6,9 +6,11 @@ #define IPC_MOJO_IPC_MOJO_HANDLE_ATTACHMENT_H_ #include "base/files/file.h" +#include "base/macros.h" +#include "build/build_config.h" #include "ipc/ipc_export.h" #include "ipc/ipc_message_attachment.h" -#include "third_party/mojo/src/mojo/public/cpp/system/handle.h" +#include "mojo/public/cpp/system/handle.h" namespace IPC { diff --git a/chromium/ipc/mojo/ipc_mojo_message_helper.cc b/chromium/ipc/mojo/ipc_mojo_message_helper.cc index 6f33f80f11b..8b8344ec766 100644 --- a/chromium/ipc/mojo/ipc_mojo_message_helper.cc +++ b/chromium/ipc/mojo/ipc_mojo_message_helper.cc @@ -4,6 +4,8 @@ #include "ipc/mojo/ipc_mojo_message_helper.h" +#include <utility> + #include "ipc/mojo/ipc_mojo_handle_attachment.h" namespace IPC { @@ -13,7 +15,7 @@ bool MojoMessageHelper::WriteMessagePipeTo( Message* message, mojo::ScopedMessagePipeHandle handle) { message->WriteAttachment(new internal::MojoHandleAttachment( - mojo::ScopedHandle::From(handle.Pass()))); + mojo::ScopedHandle::From(std::move(handle)))); return true; } diff --git a/chromium/ipc/mojo/ipc_mojo_message_helper.h b/chromium/ipc/mojo/ipc_mojo_message_helper.h index 2efa1390aee..3dc840e92e3 100644 --- a/chromium/ipc/mojo/ipc_mojo_message_helper.h +++ b/chromium/ipc/mojo/ipc_mojo_message_helper.h @@ -7,7 +7,7 @@ #include "ipc/ipc_export.h" #include "ipc/ipc_message.h" -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/system/message_pipe.h" namespace IPC { diff --git a/chromium/ipc/mojo/ipc_mojo_param_traits.h b/chromium/ipc/mojo/ipc_mojo_param_traits.h index c735fce370d..f0b8b515b6f 100644 --- a/chromium/ipc/mojo/ipc_mojo_param_traits.h +++ b/chromium/ipc/mojo/ipc_mojo_param_traits.h @@ -9,7 +9,7 @@ #include "ipc/ipc_export.h" #include "ipc/ipc_param_traits.h" -#include "third_party/mojo/src/mojo/public/cpp/system/message_pipe.h" +#include "mojo/public/cpp/system/message_pipe.h" namespace base { class PickleIterator; diff --git a/chromium/ipc/mojo/ipc_mojo_perftest.cc b/chromium/ipc/mojo/ipc_mojo_perftest.cc index 1c84f2cc919..d782c77ee59 100644 --- a/chromium/ipc/mojo/ipc_mojo_perftest.cc +++ b/chromium/ipc/mojo/ipc_mojo_perftest.cc @@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stddef.h> + #include "base/lazy_instance.h" #include "base/run_loop.h" +#include "build/build_config.h" #include "ipc/ipc_perftest_support.h" #include "ipc/mojo/ipc_channel_mojo.h" -#include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" +#include "third_party/mojo/src/mojo/edk/embedder/platform_channel_pair.h" namespace { diff --git a/chromium/ipc/mojo/run_all_unittests.cc b/chromium/ipc/mojo/run_all_unittests.cc index 63824745c0a..43a1e4e0e8d 100644 --- a/chromium/ipc/mojo/run_all_unittests.cc +++ b/chromium/ipc/mojo/run_all_unittests.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" +#include "build/build_config.h" #include "third_party/mojo/src/mojo/edk/embedder/embedder.h" #if defined(OS_ANDROID) @@ -13,30 +14,14 @@ #include "base/test/test_file_util.h" #endif -namespace { - -class NoAtExitBaseTestSuite : public base::TestSuite { - public: - NoAtExitBaseTestSuite(int argc, char** argv) - : base::TestSuite(argc, argv, false) { - } -}; - -int RunTestSuite(int argc, char** argv) { - return NoAtExitBaseTestSuite(argc, argv).Run(); -} - -} // namespace - int main(int argc, char** argv) { - mojo::embedder::Init(); #if defined(OS_ANDROID) JNIEnv* env = base::android::AttachCurrentThread(); base::RegisterContentUriTestUtils(env); -#else - base::AtExitManager at_exit; #endif - return base::LaunchUnitTestsSerially(argc, - argv, - base::Bind(&RunTestSuite, argc, argv)); + base::TestSuite test_suite(argc, argv); + mojo::embedder::Init(); + return base::LaunchUnitTestsSerially( + argc, argv, + base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); } diff --git a/chromium/ipc/mojo/scoped_ipc_support.cc b/chromium/ipc/mojo/scoped_ipc_support.cc index 572946373e3..15575444e64 100644 --- a/chromium/ipc/mojo/scoped_ipc_support.cc +++ b/chromium/ipc/mojo/scoped_ipc_support.cc @@ -4,9 +4,12 @@ #include "ipc/mojo/scoped_ipc_support.h" +#include <stddef.h> + #include "base/bind.h" #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" @@ -31,10 +34,7 @@ class IPCSupportInitializer : public mojo::embedder::ProcessDelegate { ~IPCSupportInitializer() override { DCHECK(!observer_); } void Init(scoped_refptr<base::TaskRunner> io_thread_task_runner); - void ShutDown(); - - // Forces the initializer to shut down even if scopers are still holding it. - void ForceShutdown(); + void ShutDown(bool force); private: // This watches for destruction of the MessageLoop that IPCSupportInitializer @@ -52,7 +52,7 @@ class IPCSupportInitializer : public mojo::embedder::ProcessDelegate { private: // base::MessageLoop::DestructionObserver: void WillDestroyCurrentMessageLoop() override { - initializer_->ForceShutdown(); + initializer_->ShutDown(true); } IPCSupportInitializer* initializer_; @@ -107,29 +107,21 @@ void IPCSupportInitializer::Init( io_thread_task_runner_->PostTask( FROM_HERE, base::Bind(&WatchMessageLoopOnIOThread, observer_)); mojo::embedder::InitIPCSupport( - mojo::embedder::ProcessType::NONE, io_thread_task_runner_, this, - io_thread_task_runner_, mojo::embedder::ScopedPlatformHandle()); + mojo::embedder::ProcessType::NONE, this, io_thread_task_runner_, + mojo::embedder::ScopedPlatformHandle()); } } -void IPCSupportInitializer::ShutDown() { - { - base::AutoLock locker(lock_); - if (shutting_down_ || was_shut_down_) - return; - DCHECK(init_count_ > 0); - if (init_count_ > 1) { - init_count_--; - return; - } - } - ForceShutdown(); -} - -void IPCSupportInitializer::ForceShutdown() { +void IPCSupportInitializer::ShutDown(bool force) { base::AutoLock locker(lock_); if (shutting_down_ || was_shut_down_) return; + DCHECK(init_count_ > 0); + if (init_count_ > 1 && !force) { + init_count_--; + return; + } + shutting_down_ = true; if (base::MessageLoop::current() && base::MessageLoop::current()->task_runner() == io_thread_task_runner_) { @@ -173,7 +165,7 @@ ScopedIPCSupport::ScopedIPCSupport( } ScopedIPCSupport::~ScopedIPCSupport() { - ipc_support_initializer.Get().ShutDown(); + ipc_support_initializer.Get().ShutDown(false); } } // namespace IPC diff --git a/chromium/ipc/placeholder_brokerable_attachment.h b/chromium/ipc/placeholder_brokerable_attachment.h index 7a63c729bb0..a8b08ef5ae5 100644 --- a/chromium/ipc/placeholder_brokerable_attachment.h +++ b/chromium/ipc/placeholder_brokerable_attachment.h @@ -5,6 +5,7 @@ #ifndef IPC_PLACEHOLDER_BROKERABLE_ATTACHMENT_H_ #define IPC_PLACEHOLDER_BROKERABLE_ATTACHMENT_H_ +#include "base/macros.h" #include "ipc/brokerable_attachment.h" #include "ipc/ipc_export.h" diff --git a/chromium/ipc/run_all_unittests.cc b/chromium/ipc/run_all_unittests.cc index a488802156a..26a73955eaf 100644 --- a/chromium/ipc/run_all_unittests.cc +++ b/chromium/ipc/run_all_unittests.cc @@ -2,39 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/at_exit.h" #include "base/bind.h" #include "base/test/launcher/unit_test_launcher.h" #include "base/test/test_suite.h" +#include "build/build_config.h" #if defined(OS_ANDROID) #include "base/android/jni_android.h" #include "base/test/test_file_util.h" #endif -namespace { - -class NoAtExitBaseTestSuite : public base::TestSuite { - public: - NoAtExitBaseTestSuite(int argc, char** argv) - : base::TestSuite(argc, argv, false) { - } -}; - -int RunTestSuite(int argc, char** argv) { - return NoAtExitBaseTestSuite(argc, argv).Run(); -} - -} // namespace - int main(int argc, char** argv) { #if defined(OS_ANDROID) JNIEnv* env = base::android::AttachCurrentThread(); base::RegisterContentUriTestUtils(env); -#else - base::AtExitManager at_exit; #endif - return base::LaunchUnitTestsSerially(argc, - argv, - base::Bind(&RunTestSuite, argc, argv)); + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, argv, + base::Bind(&base::TestSuite::Run, base::Unretained(&test_suite))); } diff --git a/chromium/ipc/sync_socket_unittest.cc b/chromium/ipc/sync_socket_unittest.cc index 9bf19dd6f86..89c155b0613 100644 --- a/chromium/ipc/sync_socket_unittest.cc +++ b/chromium/ipc/sync_socket_unittest.cc @@ -4,14 +4,17 @@ #include "base/sync_socket.h" +#include <stddef.h> #include <stdio.h> #include <sstream> #include <string> #include "base/bind.h" #include "base/location.h" +#include "base/macros.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread.h" +#include "build/build_config.h" #include "ipc/ipc_test_base.h" #include "testing/gtest/include/gtest/gtest.h" @@ -95,9 +98,7 @@ class SyncSocketServerListener : public IPC::Listener { // When the client responds, it sends back a shutdown message, // which causes the message loop to exit. - void OnMsgClassShutdown() { - base::MessageLoop::current()->Quit(); - } + void OnMsgClassShutdown() { base::MessageLoop::current()->QuitWhenIdle(); } IPC::Channel* chan_; @@ -153,7 +154,7 @@ class SyncSocketClientListener : public IPC::Listener { EXPECT_EQ(0U, socket_->Peek()); IPC::Message* msg = new MsgClassShutdown(); EXPECT_TRUE(chan_->Send(msg)); - base::MessageLoop::current()->Quit(); + base::MessageLoop::current()->QuitWhenIdle(); } base::SyncSocket* socket_; diff --git a/chromium/ipc/test_util_mac.cc b/chromium/ipc/test_util_mac.cc new file mode 100644 index 00000000000..6b6e64bb4af --- /dev/null +++ b/chromium/ipc/test_util_mac.cc @@ -0,0 +1,143 @@ +// 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/test_util_mac.h" + +#include <mach/mach_vm.h> +#include <servers/bootstrap.h> +#include <stddef.h> + +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/rand_util.h" +#include "base/strings/stringprintf.h" + +namespace { + +// Structs used to pass a Mach port over mach_msg(). +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; +}; + +} // namespace + +namespace IPC { + +std::string CreateRandomServiceName() { + return base::StringPrintf("TestUtilMac.%llu", base::RandUint64()); +} + +base::mac::ScopedMachReceiveRight 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::ScopedMachReceiveRight(port); +} + +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(), + MACH_PORT_RIGHT_RECEIVE, &client_port); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "MakeReceivingPort"; + return base::mac::ScopedMachReceiveRight(client_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"; +} + +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; +} + +mach_port_urefs_t GetMachRefCount(mach_port_name_t name, + mach_port_right_t right) { + mach_port_urefs_t ref_count; + kern_return_t kr = mach_port_get_refs(mach_task_self(), name, + MACH_PORT_RIGHT_SEND, &ref_count); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "GetRefCount"; + return ref_count; +} + +void IncrementMachRefCount(mach_port_name_t name, mach_port_right_t right) { + kern_return_t kr = mach_port_mod_refs(mach_task_self(), name, right, 1); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "GetRefCount"; +} + +bool GetMachProtections(void* address, size_t size, int* current, int* max) { + vm_region_info_t region_info; + mach_vm_address_t mem_address = reinterpret_cast<mach_vm_address_t>(address); + mach_vm_size_t mem_size = size; + vm_region_basic_info_64 basic_info; + + region_info = reinterpret_cast<vm_region_recurse_info_t>(&basic_info); + vm_region_flavor_t flavor = VM_REGION_BASIC_INFO_64; + memory_object_name_t memory_object; + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + + kern_return_t kr = + mach_vm_region(mach_task_self(), &mem_address, &mem_size, flavor, + region_info, &count, &memory_object); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "Failed to get region info."; + return false; + } + + *current = basic_info.protection; + *max = basic_info.max_protection; + return true; +} + +} // namespace IPC diff --git a/chromium/ipc/test_util_mac.h b/chromium/ipc/test_util_mac.h new file mode 100644 index 00000000000..2f5e819dd45 --- /dev/null +++ b/chromium/ipc/test_util_mac.h @@ -0,0 +1,61 @@ +// 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. + +// This file contains Mac-specific utility functions used by multiple test +// suites. + +#ifndef IPC_TEST_UTIL_MAC_H_ +#define IPC_TEST_UTIL_MAC_H_ + +#include <mach/mach.h> +#include <stddef.h> + +#include <string> + +#include "base/mac/scoped_mach_port.h" + +namespace IPC { + +// Returns a random name suitable for Mach Server registration. +std::string CreateRandomServiceName(); + +// Makes the current process into a Mach Server with the given |service_name|. +// Returns a receive right for the service port. +base::mac::ScopedMachReceiveRight BecomeMachServer(const char* service_name); + +// Returns a send right to the service port for the Mach Server with the given +// |service_name|. +base::mac::ScopedMachSendRight LookupServer(const char* service_name); + +// Returns the receive right to a newly minted Mach port. +base::mac::ScopedMachReceiveRight MakeReceivingPort(); + +// Blocks until a Mach message is sent to |port_to_listen_on|. This Mach message +// must contain a Mach port. Returns that Mach port. +base::mac::ScopedMachSendRight ReceiveMachPort(mach_port_t port_to_listen_on); + +// Passes a copy of the send right of |port_to_send| to |receiving_port|, using +// the given |disposition|. +void SendMachPort(mach_port_t receiving_port, + mach_port_t port_to_send, + int disposition); + +// The number of active names in the current task's port name space. +mach_msg_type_number_t GetActiveNameCount(); + +// The number of references the current task has for a given name. +mach_port_urefs_t GetMachRefCount(mach_port_name_t name, + mach_port_right_t right); + +// Increments the ref count for the right/name pair. +void IncrementMachRefCount(mach_port_name_t name, mach_port_right_t right); + +// Gets the current and maximum protection levels of the memory region. +// Returns whether the operation was successful. +// |current| and |max| are output variables only populated on success. +bool GetMachProtections(void* address, size_t size, int* current, int* max); + +} // namespace IPC + +#endif // IPC_TEST_UTIL_MAC_H_ diff --git a/chromium/ipc/unix_domain_socket_util.cc b/chromium/ipc/unix_domain_socket_util.cc index 74053445b7a..fb64cb29165 100644 --- a/chromium/ipc/unix_domain_socket_util.cc +++ b/chromium/ipc/unix_domain_socket_util.cc @@ -6,6 +6,7 @@ #include <errno.h> #include <fcntl.h> +#include <stddef.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/un.h> @@ -16,6 +17,7 @@ #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" namespace IPC { diff --git a/chromium/ipc/unix_domain_socket_util.h b/chromium/ipc/unix_domain_socket_util.h index 5752364fa10..13590722950 100644 --- a/chromium/ipc/unix_domain_socket_util.h +++ b/chromium/ipc/unix_domain_socket_util.h @@ -5,6 +5,7 @@ #ifndef IPC_UNIX_DOMAIN_SOCKET_UTIL_H_ #define IPC_UNIX_DOMAIN_SOCKET_UTIL_H_ +#include <stddef.h> #include <sys/types.h> #include <string> diff --git a/chromium/ipc/unix_domain_socket_util_unittest.cc b/chromium/ipc/unix_domain_socket_util_unittest.cc index 57365a5840d..49c1c024c1f 100644 --- a/chromium/ipc/unix_domain_socket_util_unittest.cc +++ b/chromium/ipc/unix_domain_socket_util_unittest.cc @@ -2,11 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <stddef.h> #include <sys/socket.h> #include "base/bind.h" #include "base/files/file_path.h" #include "base/location.h" +#include "base/macros.h" #include "base/path_service.h" #include "base/posix/eintr_wrapper.h" #include "base/single_thread_task_runner.h" |