1 // Copyright (c) 2012 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 7 #if defined(OS_POSIX) 8 #if defined(OS_MACOSX) 9 extern "C" { 10 #include <sandbox.h> 11 } 12 #endif 13 #include <fcntl.h> 14 #include <sys/socket.h> 15 #include <sys/stat.h> 16 #include <unistd.h> 17 18 #include <queue> 19 20 #include "base/callback.h" 21 #include "base/file_descriptor_posix.h" 22 #include "base/message_loop/message_loop.h" 23 #include "base/pickle.h" 24 #include "base/posix/eintr_wrapper.h" 25 #include "base/synchronization/waitable_event.h" 26 #include "ipc/ipc_message_utils.h" 27 #include "ipc/ipc_test_base.h" 28 29 namespace { 30 31 const unsigned kNumFDsToSend = 20; 32 const char* kDevZeroPath = "/dev/zero"; 33 34 class MyChannelDescriptorListenerBase : public IPC::Listener { 35 public: 36 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { 37 PickleIterator iter(message); 38 39 base::FileDescriptor descriptor; 40 41 IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor); 42 43 HandleFD(descriptor.fd); 44 return true; 45 } 46 47 protected: 48 virtual void HandleFD(int fd) = 0; 49 }; 50 51 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase { 52 public: 53 explicit MyChannelDescriptorListener(ino_t expected_inode_num) 54 : MyChannelDescriptorListenerBase(), 55 expected_inode_num_(expected_inode_num), 56 num_fds_received_(0) { 57 } 58 59 bool GotExpectedNumberOfDescriptors() const { 60 return num_fds_received_ == kNumFDsToSend; 61 } 62 63 virtual void OnChannelError() OVERRIDE { 64 base::MessageLoop::current()->Quit(); 65 } 66 67 protected: 68 virtual void HandleFD(int fd) OVERRIDE { 69 // Check that we can read from the FD. 70 char buf; 71 ssize_t amt_read = read(fd, &buf, 1); 72 ASSERT_EQ(amt_read, 1); 73 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes. 74 75 struct stat st; 76 ASSERT_EQ(fstat(fd, &st), 0); 77 78 ASSERT_EQ(close(fd), 0); 79 80 // Compare inode numbers to check that the file sent over the wire is 81 // actually the one expected. 82 ASSERT_EQ(expected_inode_num_, st.st_ino); 83 84 ++num_fds_received_; 85 if (num_fds_received_ == kNumFDsToSend) 86 base::MessageLoop::current()->Quit(); 87 } 88 89 private: 90 ino_t expected_inode_num_; 91 unsigned num_fds_received_; 92 }; 93 94 95 class IPCSendFdsTest : public IPCTestBase { 96 protected: 97 void RunServer() { 98 // Set up IPC channel and start client. 99 MyChannelDescriptorListener listener(-1); 100 CreateChannel(&listener); 101 ASSERT_TRUE(ConnectChannel()); 102 ASSERT_TRUE(StartClient()); 103 104 for (unsigned i = 0; i < kNumFDsToSend; ++i) { 105 const int fd = open(kDevZeroPath, O_RDONLY); 106 ASSERT_GE(fd, 0); 107 base::FileDescriptor descriptor(fd, true); 108 109 IPC::Message* message = 110 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL); 111 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor); 112 ASSERT_TRUE(sender()->Send(message)); 113 } 114 115 // Run message loop. 116 base::MessageLoop::current()->Run(); 117 118 // Close the channel so the client's OnChannelError() gets fired. 119 channel()->Close(); 120 121 EXPECT_TRUE(WaitForClientShutdown()); 122 DestroyChannel(); 123 } 124 }; 125 126 TEST_F(IPCSendFdsTest, DescriptorTest) { 127 Init("SendFdsClient"); 128 RunServer(); 129 } 130 131 int SendFdsClientCommon(const std::string& test_client_name, 132 ino_t expected_inode_num) { 133 base::MessageLoopForIO main_message_loop; 134 MyChannelDescriptorListener listener(expected_inode_num); 135 136 // Set up IPC channel. 137 IPC::Channel channel(IPCTestBase::GetChannelName(test_client_name), 138 IPC::Channel::MODE_CLIENT, 139 &listener); 140 CHECK(channel.Connect()); 141 142 // Run message loop. 143 base::MessageLoop::current()->Run(); 144 145 // Verify that the message loop was exited due to getting the correct number 146 // of descriptors, and not because of the channel closing unexpectedly. 147 CHECK(listener.GotExpectedNumberOfDescriptors()); 148 149 return 0; 150 } 151 152 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) { 153 struct stat st; 154 int fd = open(kDevZeroPath, O_RDONLY); 155 fstat(fd, &st); 156 EXPECT_GE(IGNORE_EINTR(close(fd)), 0); 157 return SendFdsClientCommon("SendFdsClient", st.st_ino); 158 } 159 160 #if defined(OS_MACOSX) 161 // Test that FDs are correctly sent to a sandboxed process. 162 // TODO(port): Make this test cross-platform. 163 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) { 164 Init("SendFdsSandboxedClient"); 165 RunServer(); 166 } 167 168 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) { 169 struct stat st; 170 const int fd = open(kDevZeroPath, O_RDONLY); 171 fstat(fd, &st); 172 if (IGNORE_EINTR(close(fd)) < 0) 173 return -1; 174 175 // Enable the sandbox. 176 char* error_buff = NULL; 177 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, 178 &error_buff); 179 bool success = (error == 0 && error_buff == NULL); 180 if (!success) 181 return -1; 182 183 sandbox_free_error(error_buff); 184 185 // Make sure sandbox is really enabled. 186 if (open(kDevZeroPath, O_RDONLY) != -1) { 187 LOG(ERROR) << "Sandbox wasn't properly enabled"; 188 return -1; 189 } 190 191 // See if we can receive a file descriptor. 192 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino); 193 } 194 #endif // defined(OS_MACOSX) 195 196 197 class MyCBListener : public MyChannelDescriptorListenerBase { 198 public: 199 MyCBListener(base::Callback<void(int)> cb, int fds_to_send) 200 : MyChannelDescriptorListenerBase(), 201 cb_(cb) { 202 } 203 204 protected: 205 virtual void HandleFD(int fd) OVERRIDE { 206 cb_.Run(fd); 207 } 208 private: 209 base::Callback<void(int)> cb_; 210 }; 211 212 std::pair<int, int> make_socket_pair() { 213 int pipe_fds[2]; 214 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds))); 215 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]); 216 } 217 218 static void null_cb(int unused_fd) { 219 NOTREACHED(); 220 } 221 222 class PipeChannelHelper { 223 public: 224 PipeChannelHelper(base::Thread* in_thread, 225 base::Thread* out_thread, 226 base::Callback<void(int)> cb, 227 int fds_to_send) : 228 in_thread_(in_thread), 229 out_thread_(out_thread), 230 cb_listener_(cb, fds_to_send), 231 null_listener_(base::Bind(&null_cb), 0) { 232 } 233 234 void Init() { 235 IPC::ChannelHandle in_handle("IN"); 236 in.reset(new IPC::Channel(in_handle, 237 IPC::Channel::MODE_SERVER, 238 &null_listener_)); 239 base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false); 240 IPC::ChannelHandle out_handle("OUT", out_fd); 241 out.reset(new IPC::Channel(out_handle, 242 IPC::Channel::MODE_CLIENT, 243 &cb_listener_)); 244 // PostTask the connect calls to make sure the callbacks happens 245 // on the right threads. 246 in_thread_->message_loop()->PostTask( 247 FROM_HERE, 248 base::Bind(&PipeChannelHelper::Connect, in.get())); 249 out_thread_->message_loop()->PostTask( 250 FROM_HERE, 251 base::Bind(&PipeChannelHelper::Connect, out.get())); 252 } 253 254 static void DestroyChannel(scoped_ptr<IPC::Channel> *c, 255 base::WaitableEvent *event) { 256 c->reset(0); 257 event->Signal(); 258 } 259 260 ~PipeChannelHelper() { 261 base::WaitableEvent a(true, false); 262 base::WaitableEvent b(true, false); 263 in_thread_->message_loop()->PostTask( 264 FROM_HERE, 265 base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a)); 266 out_thread_->message_loop()->PostTask( 267 FROM_HERE, 268 base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b)); 269 a.Wait(); 270 b.Wait(); 271 } 272 273 static void Connect(IPC::Channel *channel) { 274 EXPECT_TRUE(channel->Connect()); 275 } 276 277 void Send(int fd) { 278 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop()); 279 280 ASSERT_GE(fd, 0); 281 base::FileDescriptor descriptor(fd, true); 282 283 IPC::Message* message = 284 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL); 285 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor); 286 ASSERT_TRUE(in->Send(message)); 287 } 288 289 private: 290 scoped_ptr<IPC::Channel> in, out; 291 base::Thread* in_thread_; 292 base::Thread* out_thread_; 293 MyCBListener cb_listener_; 294 MyCBListener null_listener_; 295 }; 296 297 // This test is meant to provoke a kernel bug on OSX, and to prove 298 // that the workaround for it is working. It sets up two pipes and three 299 // threads, the producer thread creates socketpairs and sends one of the fds 300 // over pipe1 to the middleman thread. The middleman thread simply takes the fd 301 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte 302 // to each fd it receives and then closes the pipe. The producer thread reads 303 // the bytes back from each pair of pipes and make sure that everything worked. 304 // This feedback mechanism makes sure that not too many file descriptors are 305 // in flight at the same time. For more info on the bug, see: 306 // http://crbug.com/298276 307 class IPCMultiSendingFdsTest : public testing::Test { 308 public: 309 IPCMultiSendingFdsTest() : received_(true, false) {} 310 311 void Producer(PipeChannelHelper* dest, 312 base::Thread* t, 313 int pipes_to_send) { 314 for (int i = 0; i < pipes_to_send; i++) { 315 received_.Reset(); 316 std::pair<int, int> pipe_fds = make_socket_pair(); 317 t->message_loop()->PostTask( 318 FROM_HERE, 319 base::Bind(&PipeChannelHelper::Send, 320 base::Unretained(dest), 321 pipe_fds.second)); 322 char tmp = 'x'; 323 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1))); 324 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first))); 325 received_.Wait(); 326 } 327 } 328 329 void ConsumerHandleFD(int fd) { 330 char tmp = 'y'; 331 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1))); 332 CHECK_EQ(tmp, 'x'); 333 CHECK_EQ(0, IGNORE_EINTR(close(fd))); 334 received_.Signal(); 335 } 336 337 base::Thread* CreateThread(const char* name) { 338 base::Thread* ret = new base::Thread(name); 339 base::Thread::Options options; 340 options.message_loop_type = base::MessageLoop::TYPE_IO; 341 ret->StartWithOptions(options); 342 return ret; 343 } 344 345 void Run() { 346 // On my mac, this test fails roughly 35 times per 347 // million sends with low load, but much more with high load. 348 // Unless the workaround is in place. With 10000 sends, we 349 // should see at least a 3% failure rate. 350 const int pipes_to_send = 20000; 351 scoped_ptr<base::Thread> producer(CreateThread("producer")); 352 scoped_ptr<base::Thread> middleman(CreateThread("middleman")); 353 scoped_ptr<base::Thread> consumer(CreateThread("consumer")); 354 PipeChannelHelper pipe1( 355 middleman.get(), 356 consumer.get(), 357 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD, 358 base::Unretained(this)), 359 pipes_to_send); 360 PipeChannelHelper pipe2( 361 producer.get(), 362 middleman.get(), 363 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)), 364 pipes_to_send); 365 pipe1.Init(); 366 pipe2.Init(); 367 Producer(&pipe2, producer.get(), pipes_to_send); 368 } 369 370 private: 371 base::WaitableEvent received_; 372 }; 373 374 TEST_F(IPCMultiSendingFdsTest, StressTest) { 375 Run(); 376 } 377 378 } // namespace 379 380 #endif // defined(OS_POSIX) 381