| 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> |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 | 206 |
| 207 // Make sure sandbox is really enabled. | 207 // Make sure sandbox is really enabled. |
| 208 ASSERT_EQ(-1, open(kDevZeroPath, O_RDONLY)) | 208 ASSERT_EQ(-1, open(kDevZeroPath, O_RDONLY)) |
| 209 << "Sandbox wasn't properly enabled"; | 209 << "Sandbox wasn't properly enabled"; |
| 210 | 210 |
| 211 // See if we can receive a file descriptor. | 211 // See if we can receive a file descriptor. |
| 212 SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino); | 212 SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino); |
| 213 } | 213 } |
| 214 #endif // defined(OS_MACOSX) | 214 #endif // defined(OS_MACOSX) |
| 215 | 215 |
| 216 | |
| 217 class MyCBListener : public MyChannelDescriptorListenerBase { | |
| 218 public: | |
| 219 MyCBListener(base::Callback<void(int)> cb, int fds_to_send) | |
| 220 : MyChannelDescriptorListenerBase(), | |
| 221 cb_(cb) { | |
| 222 } | |
| 223 | |
| 224 protected: | |
| 225 void HandleFD(int fd) override { cb_.Run(fd); } | |
| 226 private: | |
| 227 base::Callback<void(int)> cb_; | |
| 228 }; | |
| 229 | |
| 230 std::pair<int, int> make_socket_pair() { | |
| 231 int pipe_fds[2]; | |
| 232 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds))); | |
| 233 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]); | |
| 234 } | |
| 235 | |
| 236 static void null_cb(int unused_fd) { | |
| 237 NOTREACHED(); | |
| 238 } | |
| 239 | |
| 240 class PipeChannelHelper { | |
| 241 public: | |
| 242 PipeChannelHelper(base::Thread* in_thread, | |
| 243 base::Thread* out_thread, | |
| 244 base::Callback<void(int)> cb, | |
| 245 int fds_to_send) : | |
| 246 in_thread_(in_thread), | |
| 247 out_thread_(out_thread), | |
| 248 cb_listener_(cb, fds_to_send), | |
| 249 null_listener_(base::Bind(&null_cb), 0) { | |
| 250 } | |
| 251 | |
| 252 void Init() { | |
| 253 IPC::ChannelHandle in_handle("IN"); | |
| 254 in = IPC::Channel::CreateServer( | |
| 255 in_handle, &null_listener_, in_thread_->task_runner()); | |
| 256 IPC::ChannelHandle out_handle( | |
| 257 "OUT", base::FileDescriptor(in->TakeClientFileDescriptor())); | |
| 258 out = IPC::Channel::CreateClient( | |
| 259 out_handle, &cb_listener_, out_thread_->task_runner()); | |
| 260 // PostTask the connect calls to make sure the callbacks happens | |
| 261 // on the right threads. | |
| 262 in_thread_->task_runner()->PostTask( | |
| 263 FROM_HERE, base::Bind(&PipeChannelHelper::Connect, in.get())); | |
| 264 out_thread_->task_runner()->PostTask( | |
| 265 FROM_HERE, base::Bind(&PipeChannelHelper::Connect, out.get())); | |
| 266 } | |
| 267 | |
| 268 static void DestroyChannel(std::unique_ptr<IPC::Channel>* c, | |
| 269 base::WaitableEvent* event) { | |
| 270 c->reset(0); | |
| 271 event->Signal(); | |
| 272 } | |
| 273 | |
| 274 ~PipeChannelHelper() { | |
| 275 base::WaitableEvent a(base::WaitableEvent::ResetPolicy::MANUAL, | |
| 276 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| 277 base::WaitableEvent b(base::WaitableEvent::ResetPolicy::MANUAL, | |
| 278 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| 279 in_thread_->task_runner()->PostTask( | |
| 280 FROM_HERE, base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a)); | |
| 281 out_thread_->task_runner()->PostTask( | |
| 282 FROM_HERE, base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b)); | |
| 283 a.Wait(); | |
| 284 b.Wait(); | |
| 285 } | |
| 286 | |
| 287 static void Connect(IPC::Channel *channel) { | |
| 288 EXPECT_TRUE(channel->Connect()); | |
| 289 } | |
| 290 | |
| 291 void Send(int fd) { | |
| 292 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop()); | |
| 293 | |
| 294 ASSERT_GE(fd, 0); | |
| 295 base::FileDescriptor descriptor(fd, true); | |
| 296 | |
| 297 IPC::Message* message = | |
| 298 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL); | |
| 299 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor); | |
| 300 ASSERT_TRUE(in->Send(message)); | |
| 301 } | |
| 302 | |
| 303 private: | |
| 304 std::unique_ptr<IPC::Channel> in, out; | |
| 305 base::Thread* in_thread_; | |
| 306 base::Thread* out_thread_; | |
| 307 MyCBListener cb_listener_; | |
| 308 MyCBListener null_listener_; | |
| 309 }; | |
| 310 | |
| 311 // This test is meant to provoke a kernel bug on OSX, and to prove | |
| 312 // that the workaround for it is working. It sets up two pipes and three | |
| 313 // threads, the producer thread creates socketpairs and sends one of the fds | |
| 314 // over pipe1 to the middleman thread. The middleman thread simply takes the fd | |
| 315 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte | |
| 316 // to each fd it receives and then closes the pipe. The producer thread reads | |
| 317 // the bytes back from each pair of pipes and make sure that everything worked. | |
| 318 // This feedback mechanism makes sure that not too many file descriptors are | |
| 319 // in flight at the same time. For more info on the bug, see: | |
| 320 // http://crbug.com/298276 | |
| 321 class IPCMultiSendingFdsTest : public testing::Test { | |
| 322 public: | |
| 323 IPCMultiSendingFdsTest() | |
| 324 : received_(base::WaitableEvent::ResetPolicy::MANUAL, | |
| 325 base::WaitableEvent::InitialState::NOT_SIGNALED) {} | |
| 326 | |
| 327 void Producer(PipeChannelHelper* dest, | |
| 328 base::Thread* t, | |
| 329 int pipes_to_send) { | |
| 330 for (int i = 0; i < pipes_to_send; i++) { | |
| 331 received_.Reset(); | |
| 332 std::pair<int, int> pipe_fds = make_socket_pair(); | |
| 333 t->task_runner()->PostTask( | |
| 334 FROM_HERE, base::Bind(&PipeChannelHelper::Send, | |
| 335 base::Unretained(dest), pipe_fds.second)); | |
| 336 char tmp = 'x'; | |
| 337 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1))); | |
| 338 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first))); | |
| 339 received_.Wait(); | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 void ConsumerHandleFD(int fd) { | |
| 344 char tmp = 'y'; | |
| 345 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1))); | |
| 346 CHECK_EQ(tmp, 'x'); | |
| 347 CHECK_EQ(0, IGNORE_EINTR(close(fd))); | |
| 348 received_.Signal(); | |
| 349 } | |
| 350 | |
| 351 base::Thread* CreateThread(const char* name) { | |
| 352 base::Thread* ret = new base::Thread(name); | |
| 353 base::Thread::Options options; | |
| 354 options.message_loop_type = base::MessageLoop::TYPE_IO; | |
| 355 ret->StartWithOptions(options); | |
| 356 return ret; | |
| 357 } | |
| 358 | |
| 359 void Run() { | |
| 360 // On my mac, this test fails roughly 35 times per | |
| 361 // million sends with low load, but much more with high load. | |
| 362 // Unless the workaround is in place. With 10000 sends, we | |
| 363 // should see at least a 3% failure rate. | |
| 364 const int pipes_to_send = 20000; | |
| 365 std::unique_ptr<base::Thread> producer(CreateThread("producer")); | |
| 366 std::unique_ptr<base::Thread> middleman(CreateThread("middleman")); | |
| 367 std::unique_ptr<base::Thread> consumer(CreateThread("consumer")); | |
| 368 PipeChannelHelper pipe1( | |
| 369 middleman.get(), | |
| 370 consumer.get(), | |
| 371 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD, | |
| 372 base::Unretained(this)), | |
| 373 pipes_to_send); | |
| 374 PipeChannelHelper pipe2( | |
| 375 producer.get(), | |
| 376 middleman.get(), | |
| 377 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)), | |
| 378 pipes_to_send); | |
| 379 pipe1.Init(); | |
| 380 pipe2.Init(); | |
| 381 Producer(&pipe2, producer.get(), pipes_to_send); | |
| 382 } | |
| 383 | |
| 384 private: | |
| 385 base::WaitableEvent received_; | |
| 386 }; | |
| 387 | |
| 388 TEST_F(IPCMultiSendingFdsTest, StressTest) { | |
| 389 Run(); | |
| 390 } | |
| 391 | |
| 392 } // namespace | 216 } // namespace |
| 393 | 217 |
| 394 #endif // defined(OS_POSIX) | 218 #endif // defined(OS_POSIX) |
| OLD | NEW |