Index: ipc/ipc_send_fds_test.cc |
diff --git a/ipc/ipc_send_fds_test.cc b/ipc/ipc_send_fds_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5f399daf6aa3388f04a9a0c3ead06fdf8fb88940 |
--- /dev/null |
+++ b/ipc/ipc_send_fds_test.cc |
@@ -0,0 +1,187 @@ |
+// Copyright (c) 2009 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 "ipc/ipc_tests.h" |
+ |
+#if defined(OS_MACOSX) |
+extern "C" { |
+#include <sandbox.h> |
+} |
+#endif |
+#include <fcntl.h> |
+#include <sys/stat.h> |
+ |
+#include "base/eintr_wrapper.h" |
+#include "base/message_loop.h" |
+#include "ipc/ipc_channel.h" |
+#include "ipc/ipc_message_utils.h" |
+ |
+#if defined(OS_POSIX) |
+ |
+namespace { |
+ |
+const unsigned kNumFDsToSend = 20; |
+const char* kDevZeroPath = "/dev/zero"; |
+ |
+static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) { |
+ // Check that we can read from the FD. |
+ char buf; |
+ ssize_t amt_read = read(fd, &buf, 1); |
+ ASSERT_EQ(amt_read, 1); |
+ ASSERT_EQ(buf, 0); // /dev/zero always reads NUL bytes. |
+ |
+ struct stat st; |
+ ASSERT_EQ(fstat(fd, &st), 0); |
+ |
+ ASSERT_EQ(close(fd), 0); |
+ |
+ // We compare iNode numbers to check that the file sent over the wire |
+ // was actually the same physical file as the one we were expecting. |
+ ASSERT_EQ(inode_num, st.st_ino); |
+} |
+ |
+class MyChannelDescriptorListener : public IPC::Channel::Listener { |
+ public: |
+ MyChannelDescriptorListener(ino_t expected_inode_num) |
+ : expected_inode_num_(expected_inode_num), |
+ num_fds_received_(0) {} |
+ |
+ virtual void OnMessageReceived(const IPC::Message& message) { |
+ void* iter = NULL; |
+ |
+ ++num_fds_received_; |
+ base::FileDescriptor descriptor; |
+ |
+ ASSERT_TRUE( |
+ IPC::ParamTraits<base::FileDescriptor>::Read( |
+ &message, &iter, &descriptor)); |
+ |
+ VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_); |
+ if (num_fds_received_ == kNumFDsToSend) { |
+ MessageLoop::current()->Quit(); |
+ } |
+ } |
+ |
+ virtual void OnChannelError() { |
+ MessageLoop::current()->Quit(); |
+ } |
+ private: |
+ ino_t expected_inode_num_; |
+ unsigned num_fds_received_; |
+}; |
+ |
+void TestDescriptorServer(IPC::Channel &chan, |
+ base::ProcessHandle process_handle) { |
+ ASSERT_TRUE(process_handle); |
+ |
+ for (unsigned i = 0; i < kNumFDsToSend; ++i) { |
+ base::FileDescriptor descriptor; |
+ const int fd = open(kDevZeroPath, O_RDONLY); |
+ ASSERT_GE(fd, 0); |
+ descriptor.auto_close = true; |
+ descriptor.fd = fd; |
+ |
+ IPC::Message* message = new IPC::Message(0, // routing_id |
+ 3, // message type |
+ IPC::Message::PRIORITY_NORMAL); |
+ IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor); |
+ chan.Send(message); |
+ } |
+ |
+ // Run message loop. |
+ MessageLoop::current()->Run(); |
+ |
+ // Close Channel so client gets its OnChannelError() callback fired. |
+ chan.Close(); |
+ |
+ // Cleanup child process. |
+ EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); |
+} |
+ |
+int TestDescriptorClient(ino_t expected_inode_num) { |
+ MessageLoopForIO main_message_loop; |
+ MyChannelDescriptorListener listener(expected_inode_num); |
+ |
+ // Setup IPC channel. |
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, |
+ &listener); |
+ chan.Connect(); |
+ MessageLoop::current()->Run(); |
+ |
+ return 0; |
+} |
+ |
+} // namespace |
+ |
+// --------------------------------------------------------------------------- |
+#if defined(OS_MACOSX) |
+// TODO(port): Make this test cross-platform. |
+MULTIPROCESS_TEST_MAIN(RunTestDescriptorClientSandboxed) { |
+ struct stat st; |
+ const int fd = open(kDevZeroPath, O_RDONLY); |
+ fstat(fd, &st); |
+ HANDLE_EINTR(close(fd)); |
+ |
+ // Enable the Sandbox. |
+ char* error_buff = NULL; |
+ int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, |
+ &error_buff); |
+ bool success = (error == 0 && error_buff == NULL); |
+ if (!success) { |
+ return -1; |
+ } |
+ |
+ sandbox_free_error(error_buff); |
+ |
+ // Make sure Sandbox is really enabled. |
+ if (open(kDevZeroPath, O_RDONLY) != -1) { |
+ LOG(ERROR) << "Sandbox wasn't properly enabled"; |
+ return -1; |
+ } |
+ |
+ // See if we can receive a file descriptor. |
+ return TestDescriptorClient(st.st_ino); |
+} |
+ |
+// Test that FDs are correctly sent to a sandboxed process. |
+TEST_F(IPCChannelTest, DescriptorTestSandboxed) { |
+ // Setup IPC channel. |
+ MyChannelDescriptorListener listener(-1); |
+ |
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, |
+ &listener); |
+ chan.Connect(); |
+ |
+ base::ProcessHandle process_handle = SpawnChild( |
+ TEST_DESCRIPTOR_CLIENT_SANDBOXED, |
+ &chan); |
+ TestDescriptorServer(chan, process_handle); |
+} |
+#endif // defined(OS_MACOSX) |
+ |
+MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) { |
+ struct stat st; |
+ const int fd = open(kDevZeroPath, O_RDONLY); |
+ fstat(fd, &st); |
+ HANDLE_EINTR(close(fd)); |
+ |
+ return TestDescriptorClient(st.st_ino); |
+} |
+ |
+TEST_F(IPCChannelTest, DescriptorTest) { |
+ // Setup IPC channel. |
+ MyChannelDescriptorListener listener(-1); |
+ |
+ IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, |
+ &listener); |
+ chan.Connect(); |
+ |
+ base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT, |
+ &chan); |
+ TestDescriptorServer(chan, process_handle); |
+} |
+ |
+#endif // defined(OS_POSIX) |