Index: ipc/attachment_broker_mac_unittest.cc |
diff --git a/ipc/attachment_broker_mac_unittest.cc b/ipc/attachment_broker_mac_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..38b167dd386a97bb3e85a96f0fe7c366f87a0728 |
--- /dev/null |
+++ b/ipc/attachment_broker_mac_unittest.cc |
@@ -0,0 +1,828 @@ |
+// 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 <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/memory/scoped_ptr.h" |
+#include "base/memory/shared_memory.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[] = "Katy Perry is so cool."; |
Tom Sepez
2015/10/09 22:38:47
nit: some may frown on this level of colloquialism
erikchen
2015/10/09 23:14:43
I kept the flippancy but removed the proper nouns.
|
+const char kDataBuffer3[] = "Taylor Swift is so cool."; |
+const char kDataBuffer4[] = "Justin Bieber is so cool."; |
+const std::string g_service_switch_name = "service_name"; |
+ |
+enum TestResult { |
+ RESULT_UNKNOWN, |
+ RESULT_SUCCESS, |
+ RESULT_FAILURE, |
+}; |
+ |
+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()); |
+ return base::mac::ScopedMachSendRight( |
+ received_mach_port_attachment->get_mach_port()); |
+} |
+ |
+// 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) { |
+ 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, false)); |
+ 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)); |
+ |
+ 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); |
+ char* buffer = static_cast<char*>(malloc(contents.size())); |
Tom Sepez
2015/10/09 22:38:47
nit: scoped pointers of some flavor would help her
erikchen
2015/10/09 23:14:43
Done.
|
+ if (!base::ReadFromFD(file_descriptor.fd, buffer, contents.size())) { |
+ free(buffer); |
+ return false; |
+ } |
+ |
+ int result = memcmp(buffer, contents.c_str(), contents.size()); |
+ free(buffer); |
+ 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)); |
+ scoped_ptr<base::SharedMemory> shared_memory2(MapSharedMemoryHandle(handle2)); |
+ |
+ 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. |
+ char* start = static_cast<char*>(shared_memory1->memory()); |
Tom Sepez
2015/10/09 22:38:47
You may need volatiles and memory barriers here, i
erikchen
2015/10/09 23:14:43
Given that the store/load are happening on the sam
|
+ start[0] += 5; |
+ if (memcmp(shared_memory1->memory(), shared_memory2->memory(), |
Tom Sepez
2015/10/09 22:38:47
Would like a better test, eg. blat a known string
erikchen
2015/10/09 23:14:43
Done.
|
+ contents.size()) != 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(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() { return messages_[0]; } |
Tom Sepez
2015/10/09 22:38:47
these two should probably assert !empty.
erikchen
2015/10/09 23:14:43
Done.
|
+ void pop_first_message() { 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; |
+ } |
+ |
+ 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); |
+ } |
+ |
+ // Setup shared between tests. |
+ void CommonSetUp(const char* name) { |
+ Init(name); |
+ service_name_ = IPC::CreateRandomServiceName(); |
+ server_port_.reset(IPC::BecomeMachServer(service_name_.c_str()).release()); |
+ |
+ if (!broker_.get()) |
+ SetBroker(new IPC::AttachmentBrokerUnprivilegedMac); |
+ |
+ broker_->AddObserver(&observer_); |
+ CreateChannel(&proxy_listener_); |
+ broker_->DesignateBrokerCommunicationChannel(channel()); |
+ ASSERT_TRUE(ConnectChannel()); |
+ ASSERT_TRUE(StartClient()); |
+ |
+ client_port_.reset(IPC::ReceiveMachPort(server_port_).release()); |
+ IPC::SendMachPort(client_port_, mach_task_self(), MACH_MSG_TYPE_COPY_SEND); |
+ 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. |
+ 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_; } |
+ |
+ 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_; |
+ |
+ // The number of active names immediately after set up. |
+ mach_msg_type_number_t active_names_at_start_; |
+ |
+ std::string service_name_; |
+ |
+ ResultListener result_listener_; |
+}; |
+ |
+using OnMessageReceivedCallback = void (*)(IPC::Sender* sender, |
+ const IPC::Message& message); |
+ |
+// These objects are globally accessible, and are expected to outlive all IPC |
+// Channels. |
+struct ChildProcessGlobals { |
+ IPC::AttachmentBrokerPrivilegedMac broker; |
+ MockPortProvider port_provider; |
+ base::mac::ScopedMachSendRight server_task_port; |
+}; |
+ |
+// 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, client_port, MACH_MSG_TYPE_MAKE_SEND); |
+ |
+ // Receive the task port of the server process. |
+ base::mac::ScopedMachSendRight server_task_port( |
+ IPC::ReceiveMachPort(client_port)); |
+ |
+ scoped_ptr<ChildProcessGlobals> globals(new ChildProcessGlobals); |
+ globals->port_provider.InsertEntry(getppid(), server_task_port); |
+ globals->broker.SetPortProvider(&globals->port_provider); |
+ globals->server_task_port.reset(server_task_port.release()); |
+ 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()); |
+ |
+ while (true) { |
+ 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()) { |
+ LOG(INFO) << "Privileged process running callback."; |
+ callback(channel.get(), listener.get_first_message()); |
+ 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) { |
+ CommonSetUp("SendSharedMemoryHandle"); |
+ |
+ SendMessage1(kDataBuffer1); |
+ base::MessageLoop::current()->Run(); |
+ CommonTearDown(); |
+} |
+ |
+void SendSharedMemoryHandleCallback(IPC::Sender* sender, |
+ const IPC::Message& message) { |
+ 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) { |
+ CommonSetUp("SendSharedMemoryHandleLong"); |
+ |
+ std::string buffer(1 << 23, 'a'); |
+ SendMessage1(buffer); |
+ base::MessageLoop::current()->Run(); |
+ CommonTearDown(); |
+} |
+ |
+void SendSharedMemoryHandleLongCallback(IPC::Sender* sender, |
+ const IPC::Message& message) { |
+ 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) { |
+ CommonSetUp("SendTwoMessagesDifferentSharedMemoryHandle"); |
+ |
+ SendMessage1(kDataBuffer1); |
+ SendMessage1(kDataBuffer2); |
+ base::MessageLoop::current()->Run(); |
+ CommonTearDown(); |
+} |
+ |
+void SendTwoMessagesDifferentSharedMemoryHandleCallback( |
+ IPC::Sender* sender, |
+ const IPC::Message& message) { |
+ 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) { |
+ 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) { |
+ 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) { |
+ 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) { |
+ 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) { |
+ 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) { |
+ 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) { |
+ 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) { |
+ 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) { |
+ 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); |
+ base::MessageLoop::current()->Run(); |
+ |
+ // Get the received attachment. |
+ IPC::BrokerableAttachment::AttachmentId* id = get_observer()->get_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&) { |
+ // Do nothing special. The default behavior already runs the |
+ // AttachmentBrokerPrivilegedMac. |
+} |
+ |
+MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendSharedMemoryHandleToSelf) { |
+ return CommonPrivilegedProcessMain(&SendSharedMemoryHandleToSelfCallback, |
+ "SendSharedMemoryHandleToSelf"); |
+} |
+ |
+} // namespace |