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 |