diff options
Diffstat (limited to 'ipc/ipc_send_fds_test.cc')
-rw-r--r-- | ipc/ipc_send_fds_test.cc | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/ipc/ipc_send_fds_test.cc b/ipc/ipc_send_fds_test.cc new file mode 100644 index 0000000..5f399da --- /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) |