OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "build/build_config.h" | 5 #include "build/build_config.h" |
6 | 6 |
7 #if defined(OS_POSIX) | 7 #if defined(OS_POSIX) |
8 #if defined(OS_MACOSX) | 8 #if defined(OS_MACOSX) |
9 extern "C" { | 9 extern "C" { |
10 #include <sandbox.h> | 10 #include <sandbox.h> |
11 } | 11 } |
12 #endif | 12 #endif |
13 #include <fcntl.h> | 13 #include <fcntl.h> |
| 14 #include <sys/resource.h> |
| 15 #include <sys/socket.h> |
14 #include <sys/stat.h> | 16 #include <sys/stat.h> |
15 #include <unistd.h> | 17 #include <unistd.h> |
16 | 18 |
| 19 #include <queue> |
| 20 |
| 21 #include "base/callback.h" |
17 #include "base/file_descriptor_posix.h" | 22 #include "base/file_descriptor_posix.h" |
18 #include "base/message_loop/message_loop.h" | 23 #include "base/message_loop/message_loop.h" |
19 #include "base/pickle.h" | 24 #include "base/pickle.h" |
20 #include "base/posix/eintr_wrapper.h" | 25 #include "base/posix/eintr_wrapper.h" |
| 26 #include "base/synchronization/waitable_event.h" |
21 #include "ipc/ipc_message_utils.h" | 27 #include "ipc/ipc_message_utils.h" |
22 #include "ipc/ipc_test_base.h" | 28 #include "ipc/ipc_test_base.h" |
23 | 29 |
24 namespace { | 30 namespace { |
25 | 31 |
26 const unsigned kNumFDsToSend = 20; | 32 const unsigned kNumFDsToSend = 20; |
27 const char* kDevZeroPath = "/dev/zero"; | 33 const char* kDevZeroPath = "/dev/zero"; |
28 | 34 |
29 static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) { | 35 class MyChannelDescriptorListenerBase : public IPC::Listener { |
30 // Check that we can read from the FD. | |
31 char buf; | |
32 ssize_t amt_read = read(fd, &buf, 1); | |
33 ASSERT_EQ(amt_read, 1); | |
34 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes. | |
35 | |
36 struct stat st; | |
37 ASSERT_EQ(fstat(fd, &st), 0); | |
38 | |
39 ASSERT_EQ(close(fd), 0); | |
40 | |
41 // Compare inode numbers to check that the file sent over the wire is actually | |
42 // the one expected. | |
43 ASSERT_EQ(inode_num, st.st_ino); | |
44 } | |
45 | |
46 class MyChannelDescriptorListener : public IPC::Listener { | |
47 public: | 36 public: |
48 explicit MyChannelDescriptorListener(ino_t expected_inode_num) | |
49 : expected_inode_num_(expected_inode_num), | |
50 num_fds_received_(0) {} | |
51 | |
52 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { | 37 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { |
53 PickleIterator iter(message); | 38 PickleIterator iter(message); |
54 | 39 |
55 ++num_fds_received_; | |
56 base::FileDescriptor descriptor; | 40 base::FileDescriptor descriptor; |
57 | 41 |
58 IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor); | 42 IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor); |
59 | 43 |
60 VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_); | 44 HandleFD(descriptor.fd); |
61 if (num_fds_received_ == kNumFDsToSend) | 45 return true; |
62 base::MessageLoop::current()->Quit(); | 46 } |
63 | 47 |
64 return true; | 48 protected: |
| 49 virtual void HandleFD(int fd) = 0; |
| 50 }; |
| 51 |
| 52 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase { |
| 53 public: |
| 54 explicit MyChannelDescriptorListener(ino_t expected_inode_num) |
| 55 : MyChannelDescriptorListenerBase(), |
| 56 expected_inode_num_(expected_inode_num), |
| 57 num_fds_received_(0) { |
| 58 } |
| 59 |
| 60 bool GotExpectedNumberOfDescriptors() const { |
| 61 return num_fds_received_ == kNumFDsToSend; |
65 } | 62 } |
66 | 63 |
67 virtual void OnChannelError() OVERRIDE { | 64 virtual void OnChannelError() OVERRIDE { |
68 base::MessageLoop::current()->Quit(); | 65 base::MessageLoop::current()->Quit(); |
69 } | 66 } |
70 | 67 |
71 bool GotExpectedNumberOfDescriptors() const { | 68 protected: |
72 return num_fds_received_ == kNumFDsToSend; | 69 virtual void HandleFD(int fd) OVERRIDE { |
| 70 // Check that we can read from the FD. |
| 71 char buf; |
| 72 ssize_t amt_read = read(fd, &buf, 1); |
| 73 ASSERT_EQ(amt_read, 1); |
| 74 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes. |
| 75 |
| 76 struct stat st; |
| 77 ASSERT_EQ(fstat(fd, &st), 0); |
| 78 |
| 79 ASSERT_EQ(close(fd), 0); |
| 80 |
| 81 // Compare inode numbers to check that the file sent over the wire is |
| 82 // actually the one expected. |
| 83 ASSERT_EQ(expected_inode_num_, st.st_ino); |
| 84 |
| 85 ++num_fds_received_; |
| 86 if (num_fds_received_ == kNumFDsToSend) |
| 87 base::MessageLoop::current()->Quit(); |
73 } | 88 } |
74 | 89 |
75 private: | 90 private: |
76 ino_t expected_inode_num_; | 91 ino_t expected_inode_num_; |
77 unsigned num_fds_received_; | 92 unsigned num_fds_received_; |
78 }; | 93 }; |
79 | 94 |
| 95 |
80 class IPCSendFdsTest : public IPCTestBase { | 96 class IPCSendFdsTest : public IPCTestBase { |
81 protected: | 97 protected: |
82 void RunServer() { | 98 void RunServer() { |
83 // Set up IPC channel and start client. | 99 // Set up IPC channel and start client. |
84 MyChannelDescriptorListener listener(-1); | 100 MyChannelDescriptorListener listener(-1); |
85 CreateChannel(&listener); | 101 CreateChannel(&listener); |
86 ASSERT_TRUE(ConnectChannel()); | 102 ASSERT_TRUE(ConnectChannel()); |
87 ASSERT_TRUE(StartClient()); | 103 ASSERT_TRUE(StartClient()); |
88 | 104 |
89 for (unsigned i = 0; i < kNumFDsToSend; ++i) { | 105 for (unsigned i = 0; i < kNumFDsToSend; ++i) { |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 if (open(kDevZeroPath, O_RDONLY) != -1) { | 187 if (open(kDevZeroPath, O_RDONLY) != -1) { |
172 LOG(ERROR) << "Sandbox wasn't properly enabled"; | 188 LOG(ERROR) << "Sandbox wasn't properly enabled"; |
173 return -1; | 189 return -1; |
174 } | 190 } |
175 | 191 |
176 // See if we can receive a file descriptor. | 192 // See if we can receive a file descriptor. |
177 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino); | 193 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino); |
178 } | 194 } |
179 #endif // defined(OS_MACOSX) | 195 #endif // defined(OS_MACOSX) |
180 | 196 |
| 197 |
| 198 class MyCBListener : public MyChannelDescriptorListenerBase { |
| 199 public: |
| 200 MyCBListener(base::Callback<void(int)> cb, int fds_to_send) |
| 201 : MyChannelDescriptorListenerBase(), |
| 202 cb_(cb) { |
| 203 } |
| 204 |
| 205 protected: |
| 206 virtual void HandleFD(int fd) OVERRIDE { |
| 207 cb_.Run(fd); |
| 208 } |
| 209 private: |
| 210 base::Callback<void(int)> cb_; |
| 211 }; |
| 212 |
| 213 std::pair<int, int> make_socket_pair() { |
| 214 int pipe_fds[2]; |
| 215 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds))); |
| 216 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]); |
| 217 } |
| 218 |
| 219 static void null_cb(int unused_fd) { |
| 220 NOTREACHED(); |
| 221 } |
| 222 |
| 223 class PipeChannelHelper { |
| 224 public: |
| 225 PipeChannelHelper(base::Thread* in_thread, |
| 226 base::Thread* out_thread, |
| 227 base::Callback<void(int)> cb, |
| 228 int fds_to_send) : |
| 229 in_thread_(in_thread), |
| 230 out_thread_(out_thread), |
| 231 cb_listener_(cb, fds_to_send), |
| 232 null_listener_(base::Bind(&null_cb), 0) { |
| 233 IPC::ChannelHandle in_handle("IN"); |
| 234 in.reset(new IPC::Channel(in_handle, |
| 235 IPC::Channel::MODE_SERVER, |
| 236 &null_listener_)); |
| 237 base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false); |
| 238 IPC::ChannelHandle out_handle("OUT", out_fd); |
| 239 out.reset(new IPC::Channel(out_handle, |
| 240 IPC::Channel::MODE_CLIENT, |
| 241 &cb_listener_)); |
| 242 // PostTask the connect calls to make sure the callbacks happens |
| 243 // on the right threads. |
| 244 in_thread->message_loop()->PostTask( |
| 245 FROM_HERE, |
| 246 base::Bind(&PipeChannelHelper::Connect, in.get())); |
| 247 out_thread->message_loop()->PostTask( |
| 248 FROM_HERE, |
| 249 base::Bind(&PipeChannelHelper::Connect, out.get())); |
| 250 } |
| 251 |
| 252 static void DestroyChannel(scoped_ptr<IPC::Channel> *c, |
| 253 base::WaitableEvent *event) { |
| 254 c->reset(0); |
| 255 event->Signal(); |
| 256 } |
| 257 |
| 258 ~PipeChannelHelper() { |
| 259 base::WaitableEvent a(true, false); |
| 260 base::WaitableEvent b(true, false); |
| 261 in_thread_->message_loop()->PostTask( |
| 262 FROM_HERE, |
| 263 base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a)); |
| 264 out_thread_->message_loop()->PostTask( |
| 265 FROM_HERE, |
| 266 base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b)); |
| 267 a.Wait(); |
| 268 b.Wait(); |
| 269 } |
| 270 |
| 271 static void Connect(IPC::Channel *channel) { |
| 272 EXPECT_TRUE(channel->Connect()); |
| 273 } |
| 274 |
| 275 void Send(int fd) { |
| 276 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop()); |
| 277 |
| 278 ASSERT_GE(fd, 0); |
| 279 base::FileDescriptor descriptor(fd, true); |
| 280 |
| 281 IPC::Message* message = |
| 282 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL); |
| 283 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor); |
| 284 ASSERT_TRUE(in->Send(message)); |
| 285 } |
| 286 |
| 287 private: |
| 288 scoped_ptr<IPC::Channel> in, out; |
| 289 base::Thread* in_thread_; |
| 290 base::Thread* out_thread_; |
| 291 MyCBListener cb_listener_; |
| 292 MyCBListener null_listener_; |
| 293 }; |
| 294 |
| 295 // This test is meant to provoke a kernel bug on OSX, and to prove |
| 296 // that the workaround for it is working. It sets up two pipes and three |
| 297 // threads, the producer thread creates socketpairs and sends one of the fds |
| 298 // over pipe1 to the middleman thread. The middleman thread simply takes the fd |
| 299 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte |
| 300 // to each fd it receives and then closes the pipe. The producer thread reads |
| 301 // the bytes back from each pair of pipes and make sure that everything worked. |
| 302 // This feedback mechanism makes sure that not too many file descriptors are |
| 303 // in flight at the same time. For more info on the bug, see: |
| 304 // http://crbug.com/298276 |
| 305 class IPCMultiSendingFdsTest : public testing::Test { |
| 306 public: |
| 307 IPCMultiSendingFdsTest() : received_(true, false) {} |
| 308 |
| 309 void Producer(PipeChannelHelper* dest, |
| 310 base::Thread* t, |
| 311 int pipes_to_send) { |
| 312 for (int i = 0; i < pipes_to_send; i++) { |
| 313 received_.Reset(); |
| 314 std::pair<int, int> pipe_fds = make_socket_pair(); |
| 315 t->message_loop()->PostTask( |
| 316 FROM_HERE, |
| 317 base::Bind(&PipeChannelHelper::Send, |
| 318 base::Unretained(dest), |
| 319 pipe_fds.second)); |
| 320 char tmp = 'x'; |
| 321 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1))); |
| 322 CHECK_EQ(0, HANDLE_EINTR(close(pipe_fds.first))); |
| 323 received_.Wait(); |
| 324 } |
| 325 } |
| 326 |
| 327 void ConsumerHandleFD(int fd) { |
| 328 char tmp = 'y'; |
| 329 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1))); |
| 330 CHECK_EQ(tmp, 'x'); |
| 331 CHECK_EQ(0, HANDLE_EINTR(close(fd))); |
| 332 received_.Signal(); |
| 333 } |
| 334 |
| 335 base::Thread* CreateThread(const char* name) { |
| 336 base::Thread* ret = new base::Thread(name); |
| 337 base::Thread::Options options; |
| 338 options.message_loop_type = base::MessageLoop::TYPE_IO; |
| 339 ret->StartWithOptions(options); |
| 340 return ret; |
| 341 } |
| 342 |
| 343 void Run() { |
| 344 struct rlimit limit; |
| 345 getrlimit(RLIMIT_NOFILE, &limit); |
| 346 limit.rlim_cur = 8192; |
| 347 setrlimit(RLIMIT_NOFILE, &limit); |
| 348 // Even with 10000 sends, this test only fails one time in five or so. |
| 349 const int pipes_to_send = 50000; |
| 350 scoped_ptr<base::Thread> producer(CreateThread("producer")); |
| 351 scoped_ptr<base::Thread> middleman(CreateThread("middleman")); |
| 352 scoped_ptr<base::Thread> consumer(CreateThread("consumer")); |
| 353 PipeChannelHelper pipe1( |
| 354 middleman.get(), |
| 355 consumer.get(), |
| 356 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD, |
| 357 base::Unretained(this)), |
| 358 pipes_to_send); |
| 359 PipeChannelHelper pipe2( |
| 360 producer.get(), |
| 361 middleman.get(), |
| 362 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)), |
| 363 pipes_to_send); |
| 364 Producer(&pipe2, producer.get(), pipes_to_send); |
| 365 } |
| 366 |
| 367 private: |
| 368 base::WaitableEvent received_; |
| 369 }; |
| 370 |
| 371 TEST_F(IPCMultiSendingFdsTest, StressTest) { |
| 372 Run(); |
| 373 } |
| 374 |
181 } // namespace | 375 } // namespace |
182 | 376 |
183 #endif // defined(OS_POSIX) | 377 #endif // defined(OS_POSIX) |
OLD | NEW |