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.
5 #include "build/build_config.h"
14 #include <sys/socket.h>
20 #include "base/callback.h"
21 #include "base/file_descriptor_posix.h"
22 #include "base/location.h"
23 #include "base/pickle.h"
24 #include "base/posix/eintr_wrapper.h"
25 #include "base/single_thread_task_runner.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "ipc/ipc_message_attachment_set.h"
28 #include "ipc/ipc_message_utils.h"
29 #include "ipc/ipc_test_base.h"
32 #include "base/macros.h"
37 const unsigned kNumFDsToSend
= 7; // per message
38 const unsigned kNumMessages
= 20;
39 const char* kDevZeroPath
= "/dev/zero";
42 static_assert(kNumFDsToSend
==
43 IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage
,
44 "The number of FDs to send must be kMaxDescriptorsPerMessage.");
47 class MyChannelDescriptorListenerBase
: public IPC::Listener
{
49 bool OnMessageReceived(const IPC::Message
& message
) override
{
50 base::PickleIterator
iter(message
);
51 base::FileDescriptor descriptor
;
52 while (IPC::ParamTraits
<base::FileDescriptor
>::Read(
53 &message
, &iter
, &descriptor
)) {
54 HandleFD(descriptor
.fd
);
60 virtual void HandleFD(int fd
) = 0;
63 class MyChannelDescriptorListener
: public MyChannelDescriptorListenerBase
{
65 explicit MyChannelDescriptorListener(ino_t expected_inode_num
)
66 : MyChannelDescriptorListenerBase(),
67 expected_inode_num_(expected_inode_num
),
68 num_fds_received_(0) {
71 bool GotExpectedNumberOfDescriptors() const {
72 return num_fds_received_
== kNumFDsToSend
* kNumMessages
;
75 void OnChannelError() override
{
76 base::MessageLoop::current()->Quit();
80 void HandleFD(int fd
) override
{
82 // Check that we can read from the FD.
84 ssize_t amt_read
= read(fd
, &buf
, 1);
85 ASSERT_EQ(amt_read
, 1);
86 ASSERT_EQ(buf
, 0); // /dev/zero always reads 0 bytes.
89 ASSERT_EQ(fstat(fd
, &st
), 0);
91 ASSERT_EQ(close(fd
), 0);
93 // Compare inode numbers to check that the file sent over the wire is
94 // actually the one expected.
95 ASSERT_EQ(expected_inode_num_
, st
.st_ino
);
98 if (num_fds_received_
== kNumFDsToSend
* kNumMessages
)
99 base::MessageLoop::current()->Quit();
103 ino_t expected_inode_num_
;
104 unsigned num_fds_received_
;
108 class IPCSendFdsTest
: public IPCTestBase
{
111 // Set up IPC channel and start client.
112 MyChannelDescriptorListener
listener(-1);
113 CreateChannel(&listener
);
114 ASSERT_TRUE(ConnectChannel());
115 ASSERT_TRUE(StartClient());
117 for (unsigned i
= 0; i
< kNumMessages
; ++i
) {
118 IPC::Message
* message
=
119 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL
);
120 for (unsigned j
= 0; j
< kNumFDsToSend
; ++j
) {
121 const int fd
= open(kDevZeroPath
, O_RDONLY
);
123 base::FileDescriptor
descriptor(fd
, true);
124 IPC::ParamTraits
<base::FileDescriptor
>::Write(message
, descriptor
);
126 ASSERT_TRUE(sender()->Send(message
));
130 base::MessageLoop::current()->Run();
132 // Close the channel so the client's OnChannelError() gets fired.
135 EXPECT_TRUE(WaitForClientShutdown());
140 #if defined(OS_ANDROID)
141 #define MAYBE_DescriptorTest DISABLED_DescriptorTest
143 #define MAYBE_DescriptorTest DescriptorTest
145 TEST_F(IPCSendFdsTest
, MAYBE_DescriptorTest
) {
146 Init("SendFdsClient");
150 int SendFdsClientCommon(const std::string
& test_client_name
,
151 ino_t expected_inode_num
) {
152 base::MessageLoopForIO main_message_loop
;
153 MyChannelDescriptorListener
listener(expected_inode_num
);
155 // Set up IPC channel.
156 scoped_ptr
<IPC::Channel
> channel(IPC::Channel::CreateClient(
157 IPCTestBase::GetChannelName(test_client_name
), &listener
, nullptr));
158 CHECK(channel
->Connect());
161 base::MessageLoop::current()->Run();
163 // Verify that the message loop was exited due to getting the correct number
164 // of descriptors, and not because of the channel closing unexpectedly.
165 CHECK(listener
.GotExpectedNumberOfDescriptors());
170 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient
) {
172 int fd
= open(kDevZeroPath
, O_RDONLY
);
174 EXPECT_GE(IGNORE_EINTR(close(fd
)), 0);
175 return SendFdsClientCommon("SendFdsClient", st
.st_ino
);
178 #if defined(OS_MACOSX)
179 // Test that FDs are correctly sent to a sandboxed process.
180 // TODO(port): Make this test cross-platform.
181 TEST_F(IPCSendFdsTest
, DescriptorTestSandboxed
) {
182 Init("SendFdsSandboxedClient");
186 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient
) {
188 const int fd
= open(kDevZeroPath
, O_RDONLY
);
190 if (IGNORE_EINTR(close(fd
)) < 0)
193 // Enable the sandbox.
194 char* error_buff
= NULL
;
195 int error
= sandbox_init(kSBXProfilePureComputation
, SANDBOX_NAMED
,
197 bool success
= (error
== 0 && error_buff
== NULL
);
201 sandbox_free_error(error_buff
);
203 // Make sure sandbox is really enabled.
204 if (open(kDevZeroPath
, O_RDONLY
) != -1) {
205 LOG(ERROR
) << "Sandbox wasn't properly enabled";
209 // See if we can receive a file descriptor.
210 return SendFdsClientCommon("SendFdsSandboxedClient", st
.st_ino
);
212 #endif // defined(OS_MACOSX)
215 class MyCBListener
: public MyChannelDescriptorListenerBase
{
217 MyCBListener(base::Callback
<void(int)> cb
, int fds_to_send
)
218 : MyChannelDescriptorListenerBase(),
223 void HandleFD(int fd
) override
{ cb_
.Run(fd
); }
225 base::Callback
<void(int)> cb_
;
228 std::pair
<int, int> make_socket_pair() {
230 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX
, SOCK_STREAM
, 0, pipe_fds
)));
231 return std::pair
<int, int>(pipe_fds
[0], pipe_fds
[1]);
234 static void null_cb(int unused_fd
) {
238 class PipeChannelHelper
{
240 PipeChannelHelper(base::Thread
* in_thread
,
241 base::Thread
* out_thread
,
242 base::Callback
<void(int)> cb
,
244 in_thread_(in_thread
),
245 out_thread_(out_thread
),
246 cb_listener_(cb
, fds_to_send
),
247 null_listener_(base::Bind(&null_cb
), 0) {
251 IPC::ChannelHandle
in_handle("IN");
252 in
= IPC::Channel::CreateServer(in_handle
, &null_listener_
, nullptr);
253 IPC::ChannelHandle
out_handle(
254 "OUT", base::FileDescriptor(in
->TakeClientFileDescriptor()));
255 out
= IPC::Channel::CreateClient(out_handle
, &cb_listener_
, nullptr);
256 // PostTask the connect calls to make sure the callbacks happens
257 // on the right threads.
258 in_thread_
->task_runner()->PostTask(
259 FROM_HERE
, base::Bind(&PipeChannelHelper::Connect
, in
.get()));
260 out_thread_
->task_runner()->PostTask(
261 FROM_HERE
, base::Bind(&PipeChannelHelper::Connect
, out
.get()));
264 static void DestroyChannel(scoped_ptr
<IPC::Channel
> *c
,
265 base::WaitableEvent
*event
) {
270 ~PipeChannelHelper() {
271 base::WaitableEvent
a(true, false);
272 base::WaitableEvent
b(true, false);
273 in_thread_
->task_runner()->PostTask(
274 FROM_HERE
, base::Bind(&PipeChannelHelper::DestroyChannel
, &in
, &a
));
275 out_thread_
->task_runner()->PostTask(
276 FROM_HERE
, base::Bind(&PipeChannelHelper::DestroyChannel
, &out
, &b
));
281 static void Connect(IPC::Channel
*channel
) {
282 EXPECT_TRUE(channel
->Connect());
286 CHECK_EQ(base::MessageLoop::current(), in_thread_
->message_loop());
289 base::FileDescriptor
descriptor(fd
, true);
291 IPC::Message
* message
=
292 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL
);
293 IPC::ParamTraits
<base::FileDescriptor
>::Write(message
, descriptor
);
294 ASSERT_TRUE(in
->Send(message
));
298 scoped_ptr
<IPC::Channel
> in
, out
;
299 base::Thread
* in_thread_
;
300 base::Thread
* out_thread_
;
301 MyCBListener cb_listener_
;
302 MyCBListener null_listener_
;
305 // This test is meant to provoke a kernel bug on OSX, and to prove
306 // that the workaround for it is working. It sets up two pipes and three
307 // threads, the producer thread creates socketpairs and sends one of the fds
308 // over pipe1 to the middleman thread. The middleman thread simply takes the fd
309 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte
310 // to each fd it receives and then closes the pipe. The producer thread reads
311 // the bytes back from each pair of pipes and make sure that everything worked.
312 // This feedback mechanism makes sure that not too many file descriptors are
313 // in flight at the same time. For more info on the bug, see:
314 // http://crbug.com/298276
315 class IPCMultiSendingFdsTest
: public testing::Test
{
317 IPCMultiSendingFdsTest() : received_(true, false) {}
319 void Producer(PipeChannelHelper
* dest
,
322 for (int i
= 0; i
< pipes_to_send
; i
++) {
324 std::pair
<int, int> pipe_fds
= make_socket_pair();
325 t
->task_runner()->PostTask(
326 FROM_HERE
, base::Bind(&PipeChannelHelper::Send
,
327 base::Unretained(dest
), pipe_fds
.second
));
329 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds
.first
, &tmp
, 1)));
330 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds
.first
)));
335 void ConsumerHandleFD(int fd
) {
337 CHECK_EQ(1, HANDLE_EINTR(read(fd
, &tmp
, 1)));
339 CHECK_EQ(0, IGNORE_EINTR(close(fd
)));
343 base::Thread
* CreateThread(const char* name
) {
344 base::Thread
* ret
= new base::Thread(name
);
345 base::Thread::Options options
;
346 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
347 ret
->StartWithOptions(options
);
352 // On my mac, this test fails roughly 35 times per
353 // million sends with low load, but much more with high load.
354 // Unless the workaround is in place. With 10000 sends, we
355 // should see at least a 3% failure rate.
356 const int pipes_to_send
= 20000;
357 scoped_ptr
<base::Thread
> producer(CreateThread("producer"));
358 scoped_ptr
<base::Thread
> middleman(CreateThread("middleman"));
359 scoped_ptr
<base::Thread
> consumer(CreateThread("consumer"));
360 PipeChannelHelper
pipe1(
363 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD
,
364 base::Unretained(this)),
366 PipeChannelHelper
pipe2(
369 base::Bind(&PipeChannelHelper::Send
, base::Unretained(&pipe1
)),
373 Producer(&pipe2
, producer
.get(), pipes_to_send
);
377 base::WaitableEvent received_
;
380 TEST_F(IPCMultiSendingFdsTest
, StressTest
) {
386 #endif // defined(OS_POSIX)