Guarantee CleanUpAfterPage gets run even if CloseConnection fails.
[chromium-blink-merge.git] / ipc / ipc_send_fds_test.cc
blobc681672457a614e474e9c0c7db75279f1f4d5bdc
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_attachment_set.h"
27 #include "ipc/ipc_message_utils.h"
28 #include "ipc/ipc_test_base.h"
30 #if defined(OS_POSIX)
31 #include "base/macros.h"
32 #endif
34 namespace {
36 const unsigned kNumFDsToSend = 128; // per message
37 const unsigned kNumMessages = 3;
38 const char* kDevZeroPath = "/dev/zero";
40 #if defined(OS_POSIX)
41 static_assert(kNumFDsToSend ==
42 IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage,
43 "The number of FDs to send must be kMaxDescriptorsPerMessage.");
44 #endif
46 class MyChannelDescriptorListenerBase : public IPC::Listener {
47 public:
48 bool OnMessageReceived(const IPC::Message& message) override {
49 PickleIterator iter(message);
50 base::FileDescriptor descriptor;
51 while (IPC::ParamTraits<base::FileDescriptor>::Read(
52 &message, &iter, &descriptor)) {
53 HandleFD(descriptor.fd);
55 return true;
58 protected:
59 virtual void HandleFD(int fd) = 0;
62 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
63 public:
64 explicit MyChannelDescriptorListener(ino_t expected_inode_num)
65 : MyChannelDescriptorListenerBase(),
66 expected_inode_num_(expected_inode_num),
67 num_fds_received_(0) {
70 bool GotExpectedNumberOfDescriptors() const {
71 return num_fds_received_ == kNumFDsToSend * kNumMessages;
74 void OnChannelError() override {
75 base::MessageLoop::current()->Quit();
78 protected:
79 void HandleFD(int fd) override {
80 ASSERT_GE(fd, 0);
81 // Check that we can read from the FD.
82 char buf;
83 ssize_t amt_read = read(fd, &buf, 1);
84 ASSERT_EQ(amt_read, 1);
85 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
87 struct stat st;
88 ASSERT_EQ(fstat(fd, &st), 0);
90 ASSERT_EQ(close(fd), 0);
92 // Compare inode numbers to check that the file sent over the wire is
93 // actually the one expected.
94 ASSERT_EQ(expected_inode_num_, st.st_ino);
96 ++num_fds_received_;
97 if (num_fds_received_ == kNumFDsToSend * kNumMessages)
98 base::MessageLoop::current()->Quit();
101 private:
102 ino_t expected_inode_num_;
103 unsigned num_fds_received_;
107 class IPCSendFdsTest : public IPCTestBase {
108 protected:
109 void RunServer() {
110 // Set up IPC channel and start client.
111 MyChannelDescriptorListener listener(-1);
112 CreateChannel(&listener);
113 ASSERT_TRUE(ConnectChannel());
114 ASSERT_TRUE(StartClient());
116 for (unsigned i = 0; i < kNumMessages; ++i) {
117 IPC::Message* message =
118 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
119 for (unsigned j = 0; j < kNumFDsToSend; ++j) {
120 const int fd = open(kDevZeroPath, O_RDONLY);
121 ASSERT_GE(fd, 0);
122 base::FileDescriptor descriptor(fd, true);
123 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
125 ASSERT_TRUE(sender()->Send(message));
128 // Run message loop.
129 base::MessageLoop::current()->Run();
131 // Close the channel so the client's OnChannelError() gets fired.
132 channel()->Close();
134 EXPECT_TRUE(WaitForClientShutdown());
135 DestroyChannel();
139 TEST_F(IPCSendFdsTest, DescriptorTest) {
140 Init("SendFdsClient");
141 RunServer();
144 int SendFdsClientCommon(const std::string& test_client_name,
145 ino_t expected_inode_num) {
146 base::MessageLoopForIO main_message_loop;
147 MyChannelDescriptorListener listener(expected_inode_num);
149 // Set up IPC channel.
150 scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient(
151 IPCTestBase::GetChannelName(test_client_name),
152 &listener));
153 CHECK(channel->Connect());
155 // Run message loop.
156 base::MessageLoop::current()->Run();
158 // Verify that the message loop was exited due to getting the correct number
159 // of descriptors, and not because of the channel closing unexpectedly.
160 CHECK(listener.GotExpectedNumberOfDescriptors());
162 return 0;
165 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
166 struct stat st;
167 int fd = open(kDevZeroPath, O_RDONLY);
168 fstat(fd, &st);
169 EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
170 return SendFdsClientCommon("SendFdsClient", st.st_ino);
173 #if defined(OS_MACOSX)
174 // Test that FDs are correctly sent to a sandboxed process.
175 // TODO(port): Make this test cross-platform.
176 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
177 Init("SendFdsSandboxedClient");
178 RunServer();
181 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
182 struct stat st;
183 const int fd = open(kDevZeroPath, O_RDONLY);
184 fstat(fd, &st);
185 if (IGNORE_EINTR(close(fd)) < 0)
186 return -1;
188 // Enable the sandbox.
189 char* error_buff = NULL;
190 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
191 &error_buff);
192 bool success = (error == 0 && error_buff == NULL);
193 if (!success)
194 return -1;
196 sandbox_free_error(error_buff);
198 // Make sure sandbox is really enabled.
199 if (open(kDevZeroPath, O_RDONLY) != -1) {
200 LOG(ERROR) << "Sandbox wasn't properly enabled";
201 return -1;
204 // See if we can receive a file descriptor.
205 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
207 #endif // defined(OS_MACOSX)
210 class MyCBListener : public MyChannelDescriptorListenerBase {
211 public:
212 MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
213 : MyChannelDescriptorListenerBase(),
214 cb_(cb) {
217 protected:
218 void HandleFD(int fd) override { cb_.Run(fd); }
219 private:
220 base::Callback<void(int)> cb_;
223 std::pair<int, int> make_socket_pair() {
224 int pipe_fds[2];
225 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
226 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
229 static void null_cb(int unused_fd) {
230 NOTREACHED();
233 class PipeChannelHelper {
234 public:
235 PipeChannelHelper(base::Thread* in_thread,
236 base::Thread* out_thread,
237 base::Callback<void(int)> cb,
238 int fds_to_send) :
239 in_thread_(in_thread),
240 out_thread_(out_thread),
241 cb_listener_(cb, fds_to_send),
242 null_listener_(base::Bind(&null_cb), 0) {
245 void Init() {
246 IPC::ChannelHandle in_handle("IN");
247 in = IPC::Channel::CreateServer(in_handle, &null_listener_);
248 IPC::ChannelHandle out_handle(
249 "OUT", base::FileDescriptor(in->TakeClientFileDescriptor()));
250 out = IPC::Channel::CreateClient(out_handle, &cb_listener_);
251 // PostTask the connect calls to make sure the callbacks happens
252 // on the right threads.
253 in_thread_->message_loop()->PostTask(
254 FROM_HERE,
255 base::Bind(&PipeChannelHelper::Connect, in.get()));
256 out_thread_->message_loop()->PostTask(
257 FROM_HERE,
258 base::Bind(&PipeChannelHelper::Connect, out.get()));
261 static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
262 base::WaitableEvent *event) {
263 c->reset(0);
264 event->Signal();
267 ~PipeChannelHelper() {
268 base::WaitableEvent a(true, false);
269 base::WaitableEvent b(true, false);
270 in_thread_->message_loop()->PostTask(
271 FROM_HERE,
272 base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
273 out_thread_->message_loop()->PostTask(
274 FROM_HERE,
275 base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
276 a.Wait();
277 b.Wait();
280 static void Connect(IPC::Channel *channel) {
281 EXPECT_TRUE(channel->Connect());
284 void Send(int fd) {
285 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
287 ASSERT_GE(fd, 0);
288 base::FileDescriptor descriptor(fd, true);
290 IPC::Message* message =
291 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
292 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
293 ASSERT_TRUE(in->Send(message));
296 private:
297 scoped_ptr<IPC::Channel> in, out;
298 base::Thread* in_thread_;
299 base::Thread* out_thread_;
300 MyCBListener cb_listener_;
301 MyCBListener null_listener_;
304 // This test is meant to provoke a kernel bug on OSX, and to prove
305 // that the workaround for it is working. It sets up two pipes and three
306 // threads, the producer thread creates socketpairs and sends one of the fds
307 // over pipe1 to the middleman thread. The middleman thread simply takes the fd
308 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte
309 // to each fd it receives and then closes the pipe. The producer thread reads
310 // the bytes back from each pair of pipes and make sure that everything worked.
311 // This feedback mechanism makes sure that not too many file descriptors are
312 // in flight at the same time. For more info on the bug, see:
313 // http://crbug.com/298276
314 class IPCMultiSendingFdsTest : public testing::Test {
315 public:
316 IPCMultiSendingFdsTest() : received_(true, false) {}
318 void Producer(PipeChannelHelper* dest,
319 base::Thread* t,
320 int pipes_to_send) {
321 for (int i = 0; i < pipes_to_send; i++) {
322 received_.Reset();
323 std::pair<int, int> pipe_fds = make_socket_pair();
324 t->message_loop()->PostTask(
325 FROM_HERE,
326 base::Bind(&PipeChannelHelper::Send,
327 base::Unretained(dest),
328 pipe_fds.second));
329 char tmp = 'x';
330 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
331 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first)));
332 received_.Wait();
336 void ConsumerHandleFD(int fd) {
337 char tmp = 'y';
338 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
339 CHECK_EQ(tmp, 'x');
340 CHECK_EQ(0, IGNORE_EINTR(close(fd)));
341 received_.Signal();
344 base::Thread* CreateThread(const char* name) {
345 base::Thread* ret = new base::Thread(name);
346 base::Thread::Options options;
347 options.message_loop_type = base::MessageLoop::TYPE_IO;
348 ret->StartWithOptions(options);
349 return ret;
352 void Run() {
353 // On my mac, this test fails roughly 35 times per
354 // million sends with low load, but much more with high load.
355 // Unless the workaround is in place. With 10000 sends, we
356 // should see at least a 3% failure rate.
357 const int pipes_to_send = 20000;
358 scoped_ptr<base::Thread> producer(CreateThread("producer"));
359 scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
360 scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
361 PipeChannelHelper pipe1(
362 middleman.get(),
363 consumer.get(),
364 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
365 base::Unretained(this)),
366 pipes_to_send);
367 PipeChannelHelper pipe2(
368 producer.get(),
369 middleman.get(),
370 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
371 pipes_to_send);
372 pipe1.Init();
373 pipe2.Init();
374 Producer(&pipe2, producer.get(), pipes_to_send);
377 private:
378 base::WaitableEvent received_;
381 TEST_F(IPCMultiSendingFdsTest, StressTest) {
382 Run();
385 } // namespace
387 #endif // defined(OS_POSIX)