[Files.app] Fix the test failure due to r232408
[chromium-blink-merge.git] / ipc / ipc_send_fds_test.cc
blob7e36e73bddd35091a03519666ebbcac423b4fba5
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"
7 #if defined(OS_POSIX)
8 #if defined(OS_MACOSX)
9 extern "C" {
10 #include <sandbox.h>
12 #endif
13 #include <fcntl.h>
14 #include <sys/socket.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
18 #include <queue>
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"
29 namespace {
31 const unsigned kNumFDsToSend = 20;
32 const char* kDevZeroPath = "/dev/zero";
34 class MyChannelDescriptorListenerBase : public IPC::Listener {
35 public:
36 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
37 PickleIterator iter(message);
39 base::FileDescriptor descriptor;
41 IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor);
43 HandleFD(descriptor.fd);
44 return true;
47 protected:
48 virtual void HandleFD(int fd) = 0;
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) {
59 bool GotExpectedNumberOfDescriptors() const {
60 return num_fds_received_ == kNumFDsToSend;
63 virtual void OnChannelError() OVERRIDE {
64 base::MessageLoop::current()->Quit();
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.
75 struct stat st;
76 ASSERT_EQ(fstat(fd, &st), 0);
78 ASSERT_EQ(close(fd), 0);
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);
84 ++num_fds_received_;
85 if (num_fds_received_ == kNumFDsToSend)
86 base::MessageLoop::current()->Quit();
89 private:
90 ino_t expected_inode_num_;
91 unsigned num_fds_received_;
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());
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);
109 IPC::Message* message = new IPC::Message(0, 3);
110 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
111 ASSERT_TRUE(sender()->Send(message));
114 // Run message loop.
115 base::MessageLoop::current()->Run();
117 // Close the channel so the client's OnChannelError() gets fired.
118 channel()->Close();
120 EXPECT_TRUE(WaitForClientShutdown());
121 DestroyChannel();
125 TEST_F(IPCSendFdsTest, DescriptorTest) {
126 Init("SendFdsClient");
127 RunServer();
130 int SendFdsClientCommon(const std::string& test_client_name,
131 ino_t expected_inode_num) {
132 base::MessageLoopForIO main_message_loop;
133 MyChannelDescriptorListener listener(expected_inode_num);
135 // Set up IPC channel.
136 IPC::Channel channel(IPCTestBase::GetChannelName(test_client_name),
137 IPC::Channel::MODE_CLIENT,
138 &listener);
139 CHECK(channel.Connect());
141 // Run message loop.
142 base::MessageLoop::current()->Run();
144 // Verify that the message loop was exited due to getting the correct number
145 // of descriptors, and not because of the channel closing unexpectedly.
146 CHECK(listener.GotExpectedNumberOfDescriptors());
148 return 0;
151 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
152 struct stat st;
153 int fd = open(kDevZeroPath, O_RDONLY);
154 fstat(fd, &st);
155 EXPECT_GE(HANDLE_EINTR(close(fd)), 0);
156 return SendFdsClientCommon("SendFdsClient", st.st_ino);
159 #if defined(OS_MACOSX)
160 // Test that FDs are correctly sent to a sandboxed process.
161 // TODO(port): Make this test cross-platform.
162 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
163 Init("SendFdsSandboxedClient");
164 RunServer();
167 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
168 struct stat st;
169 const int fd = open(kDevZeroPath, O_RDONLY);
170 fstat(fd, &st);
171 if (HANDLE_EINTR(close(fd)) < 0)
172 return -1;
174 // Enable the sandbox.
175 char* error_buff = NULL;
176 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
177 &error_buff);
178 bool success = (error == 0 && error_buff == NULL);
179 if (!success)
180 return -1;
182 sandbox_free_error(error_buff);
184 // Make sure sandbox is really enabled.
185 if (open(kDevZeroPath, O_RDONLY) != -1) {
186 LOG(ERROR) << "Sandbox wasn't properly enabled";
187 return -1;
190 // See if we can receive a file descriptor.
191 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
193 #endif // defined(OS_MACOSX)
196 class MyCBListener : public MyChannelDescriptorListenerBase {
197 public:
198 MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
199 : MyChannelDescriptorListenerBase(),
200 cb_(cb) {
203 protected:
204 virtual void HandleFD(int fd) OVERRIDE {
205 cb_.Run(fd);
207 private:
208 base::Callback<void(int)> cb_;
211 std::pair<int, int> make_socket_pair() {
212 int pipe_fds[2];
213 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
214 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
217 static void null_cb(int unused_fd) {
218 NOTREACHED();
221 class PipeChannelHelper {
222 public:
223 PipeChannelHelper(base::Thread* in_thread,
224 base::Thread* out_thread,
225 base::Callback<void(int)> cb,
226 int fds_to_send) :
227 in_thread_(in_thread),
228 out_thread_(out_thread),
229 cb_listener_(cb, fds_to_send),
230 null_listener_(base::Bind(&null_cb), 0) {
233 void Init() {
234 IPC::ChannelHandle in_handle("IN");
235 in.reset(new IPC::Channel(in_handle,
236 IPC::Channel::MODE_SERVER,
237 &null_listener_));
238 base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false);
239 IPC::ChannelHandle out_handle("OUT", out_fd);
240 out.reset(new IPC::Channel(out_handle,
241 IPC::Channel::MODE_CLIENT,
242 &cb_listener_));
243 // PostTask the connect calls to make sure the callbacks happens
244 // on the right threads.
245 in_thread_->message_loop()->PostTask(
246 FROM_HERE,
247 base::Bind(&PipeChannelHelper::Connect, in.get()));
248 out_thread_->message_loop()->PostTask(
249 FROM_HERE,
250 base::Bind(&PipeChannelHelper::Connect, out.get()));
253 static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
254 base::WaitableEvent *event) {
255 c->reset(0);
256 event->Signal();
259 ~PipeChannelHelper() {
260 base::WaitableEvent a(true, false);
261 base::WaitableEvent b(true, false);
262 in_thread_->message_loop()->PostTask(
263 FROM_HERE,
264 base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
265 out_thread_->message_loop()->PostTask(
266 FROM_HERE,
267 base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
268 a.Wait();
269 b.Wait();
272 static void Connect(IPC::Channel *channel) {
273 EXPECT_TRUE(channel->Connect());
276 void Send(int fd) {
277 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
279 ASSERT_GE(fd, 0);
280 base::FileDescriptor descriptor(fd, true);
282 IPC::Message* message = new IPC::Message(0, 3);
283 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
284 ASSERT_TRUE(in->Send(message));
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_;
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) {}
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();
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();
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;
343 void Run() {
344 // On my mac, this test fails roughly 35 times per
345 // million sends with low load, but much more with high load.
346 // Unless the workaround is in place. With 10000 sends, we
347 // should see at least a 3% failure rate.
348 const int pipes_to_send = 20000;
349 scoped_ptr<base::Thread> producer(CreateThread("producer"));
350 scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
351 scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
352 PipeChannelHelper pipe1(
353 middleman.get(),
354 consumer.get(),
355 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
356 base::Unretained(this)),
357 pipes_to_send);
358 PipeChannelHelper pipe2(
359 producer.get(),
360 middleman.get(),
361 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
362 pipes_to_send);
363 pipe1.Init();
364 pipe2.Init();
365 Producer(&pipe2, producer.get(), pipes_to_send);
368 private:
369 base::WaitableEvent received_;
372 TEST_F(IPCMultiSendingFdsTest, StressTest) {
373 Run();
376 } // namespace
378 #endif // defined(OS_POSIX)