Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(454)

Unified Diff: ipc/attachment_broker_mac_unittest.cc

Issue 1397023002: ipc: Write end-to-end tests for SharedMemoryHandle brokering on Mac. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « ipc/BUILD.gn ('k') | ipc/ipc.gyp » ('j') | ipc/ipc_test_messages.h » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « ipc/BUILD.gn ('k') | ipc/ipc.gyp » ('j') | ipc/ipc_test_messages.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698