jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 1 | // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "build/build_config.h" |
| 6 | |
agl@chromium.org | 946d1b2 | 2009-07-22 23:57:21 | [diff] [blame] | 7 | #include "ipc/ipc_tests.h" |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 8 | |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 9 | #if defined(OS_MACOSX) |
| 10 | extern "C" { |
| 11 | #include <sandbox.h> |
| 12 | } |
| 13 | #endif |
patrick@chromium.org | d67c249 | 2009-03-20 17:26:02 | [diff] [blame] | 14 | #include <fcntl.h> |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 15 | #include <sys/stat.h> |
| 16 | |
agl@chromium.org | 157c61b | 2009-05-01 21:37:31 | [diff] [blame] | 17 | #include "base/eintr_wrapper.h" |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 18 | #include "base/message_loop.h" |
agl@chromium.org | 946d1b2 | 2009-07-22 23:57:21 | [diff] [blame] | 19 | #include "ipc/ipc_channel.h" |
| 20 | #include "ipc/ipc_message_utils.h" |
brettw@chromium.org | 20e1491 | 2010-08-17 19:40:11 | [diff] [blame] | 21 | #include "testing/multiprocess_func_list.h" |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 22 | |
| 23 | #if defined(OS_POSIX) |
erg@google.com | 7a4de7a6 | 2010-08-17 18:38:24 | [diff] [blame] | 24 | #include "base/file_descriptor_posix.h" |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 25 | |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 26 | namespace { |
| 27 | |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 28 | const unsigned kNumFDsToSend = 20; |
| 29 | const char* kDevZeroPath = "/dev/zero"; |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 30 | |
| 31 | static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) { |
| 32 | // Check that we can read from the FD. |
| 33 | char buf; |
| 34 | ssize_t amt_read = read(fd, &buf, 1); |
| 35 | ASSERT_EQ(amt_read, 1); |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 36 | ASSERT_EQ(buf, 0); // /dev/zero always reads NUL bytes. |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 37 | |
| 38 | struct stat st; |
| 39 | ASSERT_EQ(fstat(fd, &st), 0); |
| 40 | |
| 41 | ASSERT_EQ(close(fd), 0); |
| 42 | |
| 43 | // We compare iNode numbers to check that the file sent over the wire |
| 44 | // was actually the same physical file as the one we were expecting. |
| 45 | ASSERT_EQ(inode_num, st.st_ino); |
| 46 | } |
| 47 | |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 48 | class MyChannelDescriptorListener : public IPC::Channel::Listener { |
| 49 | public: |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 50 | MyChannelDescriptorListener(ino_t expected_inode_num) |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 51 | : expected_inode_num_(expected_inode_num), |
| 52 | num_fds_received_(0) {} |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 53 | |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 54 | virtual void OnMessageReceived(const IPC::Message& message) { |
| 55 | void* iter = NULL; |
| 56 | |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 57 | ++num_fds_received_; |
agl@chromium.org | 5fe733de | 2009-02-11 18:59:20 | [diff] [blame] | 58 | base::FileDescriptor descriptor; |
jeremy@chromium.org | e5a3ea3 | 2009-02-11 01:41:02 | [diff] [blame] | 59 | |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 60 | ASSERT_TRUE( |
agl@chromium.org | 5fe733de | 2009-02-11 18:59:20 | [diff] [blame] | 61 | IPC::ParamTraits<base::FileDescriptor>::Read( |
| 62 | &message, &iter, &descriptor)); |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 63 | |
| 64 | VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_); |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 65 | if (num_fds_received_ == kNumFDsToSend) { |
| 66 | MessageLoop::current()->Quit(); |
| 67 | } |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 68 | } |
| 69 | |
| 70 | virtual void OnChannelError() { |
| 71 | MessageLoop::current()->Quit(); |
| 72 | } |
dmaclach@chromium.org | d484ab5 | 2010-12-09 01:12:20 | [diff] [blame^] | 73 | |
| 74 | bool GotExpectedNumberOfDescriptors() { |
| 75 | return kNumFDsToSend == num_fds_received_; |
| 76 | } |
| 77 | |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 78 | private: |
| 79 | ino_t expected_inode_num_; |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 80 | unsigned num_fds_received_; |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 81 | }; |
| 82 | |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 83 | void TestDescriptorServer(IPC::Channel &chan, |
| 84 | base::ProcessHandle process_handle) { |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 85 | ASSERT_TRUE(process_handle); |
| 86 | |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 87 | for (unsigned i = 0; i < kNumFDsToSend; ++i) { |
| 88 | base::FileDescriptor descriptor; |
| 89 | const int fd = open(kDevZeroPath, O_RDONLY); |
| 90 | ASSERT_GE(fd, 0); |
| 91 | descriptor.auto_close = true; |
| 92 | descriptor.fd = fd; |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 93 | |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 94 | IPC::Message* message = new IPC::Message(0, // routing_id |
| 95 | 3, // message type |
| 96 | IPC::Message::PRIORITY_NORMAL); |
| 97 | IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor); |
dmaclach@chromium.org | d484ab5 | 2010-12-09 01:12:20 | [diff] [blame^] | 98 | ASSERT_TRUE(chan.Send(message)); |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 99 | } |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 100 | |
| 101 | // Run message loop. |
| 102 | MessageLoop::current()->Run(); |
| 103 | |
| 104 | // Close Channel so client gets its OnChannelError() callback fired. |
| 105 | chan.Close(); |
| 106 | |
| 107 | // Cleanup child process. |
| 108 | EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); |
| 109 | } |
| 110 | |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 111 | int TestDescriptorClient(ino_t expected_inode_num) { |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 112 | MessageLoopForIO main_message_loop; |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 113 | MyChannelDescriptorListener listener(expected_inode_num); |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 114 | |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 115 | // Setup IPC channel. |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 116 | IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, |
| 117 | &listener); |
evan@chromium.org | 39703fb | 2010-10-19 19:11:15 | [diff] [blame] | 118 | CHECK(chan.Connect()); |
dmaclach@chromium.org | d484ab5 | 2010-12-09 01:12:20 | [diff] [blame^] | 119 | |
| 120 | // Run message loop so IPC Channel can handle message IO. |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 121 | MessageLoop::current()->Run(); |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 122 | |
dmaclach@chromium.org | d484ab5 | 2010-12-09 01:12:20 | [diff] [blame^] | 123 | // Verify that the message loop was exited due to getting the correct |
| 124 | // number of descriptors, and not because the channel closing unexpectedly. |
| 125 | CHECK(listener.GotExpectedNumberOfDescriptors()); |
| 126 | |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 127 | return 0; |
| 128 | } |
| 129 | |
| 130 | } // namespace |
| 131 | |
| 132 | // --------------------------------------------------------------------------- |
| 133 | #if defined(OS_MACOSX) |
| 134 | // TODO(port): Make this test cross-platform. |
| 135 | MULTIPROCESS_TEST_MAIN(RunTestDescriptorClientSandboxed) { |
| 136 | struct stat st; |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 137 | const int fd = open(kDevZeroPath, O_RDONLY); |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 138 | fstat(fd, &st); |
thakis@chromium.org | 34f4094 | 2010-10-04 00:34:04 | [diff] [blame] | 139 | if (HANDLE_EINTR(close(fd)) < 0) { |
| 140 | return -1; |
| 141 | } |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 142 | |
| 143 | // Enable the Sandbox. |
| 144 | char* error_buff = NULL; |
| 145 | int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, |
| 146 | &error_buff); |
| 147 | bool success = (error == 0 && error_buff == NULL); |
| 148 | if (!success) { |
| 149 | return -1; |
| 150 | } |
| 151 | |
| 152 | sandbox_free_error(error_buff); |
| 153 | |
| 154 | // Make sure Sandbox is really enabled. |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 155 | if (open(kDevZeroPath, O_RDONLY) != -1) { |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 156 | LOG(ERROR) << "Sandbox wasn't properly enabled"; |
| 157 | return -1; |
| 158 | } |
| 159 | |
| 160 | // See if we can receive a file descriptor. |
| 161 | return TestDescriptorClient(st.st_ino); |
| 162 | } |
| 163 | |
| 164 | // Test that FDs are correctly sent to a sandboxed process. |
| 165 | TEST_F(IPCChannelTest, DescriptorTestSandboxed) { |
| 166 | // Setup IPC channel. |
| 167 | MyChannelDescriptorListener listener(-1); |
| 168 | |
| 169 | IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, |
| 170 | &listener); |
evan@chromium.org | 39703fb | 2010-10-19 19:11:15 | [diff] [blame] | 171 | ASSERT_TRUE(chan.Connect()); |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 172 | |
| 173 | base::ProcessHandle process_handle = SpawnChild( |
| 174 | TEST_DESCRIPTOR_CLIENT_SANDBOXED, |
| 175 | &chan); |
| 176 | TestDescriptorServer(chan, process_handle); |
| 177 | } |
| 178 | #endif // defined(OS_MACOSX) |
| 179 | |
| 180 | MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) { |
| 181 | struct stat st; |
agl@chromium.org | 92639446 | 2009-02-11 23:23:12 | [diff] [blame] | 182 | const int fd = open(kDevZeroPath, O_RDONLY); |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 183 | fstat(fd, &st); |
hans@chromium.org | 460da039 | 2010-09-16 08:15:32 | [diff] [blame] | 184 | EXPECT_GE(HANDLE_EINTR(close(fd)), 0); |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 185 | |
| 186 | return TestDescriptorClient(st.st_ino); |
| 187 | } |
| 188 | |
| 189 | TEST_F(IPCChannelTest, DescriptorTest) { |
| 190 | // Setup IPC channel. |
| 191 | MyChannelDescriptorListener listener(-1); |
| 192 | |
| 193 | IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, |
| 194 | &listener); |
evan@chromium.org | 39703fb | 2010-10-19 19:11:15 | [diff] [blame] | 195 | ASSERT_TRUE(chan.Connect()); |
jeremy@chromium.org | e8351b7e | 2009-02-10 22:25:39 | [diff] [blame] | 196 | |
| 197 | base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT, |
| 198 | &chan); |
| 199 | TestDescriptorServer(chan, process_handle); |
jeremy@chromium.org | c2f10ed | 2009-02-10 00:52:57 | [diff] [blame] | 200 | } |
| 201 | |
| 202 | #endif // defined(OS_POSIX) |