blob: 1df6be98643f04580e18a8ff2265fd777eca6c2e [file] [log] [blame]
jbates@chromium.org0fc87362012-03-08 05:42:56 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "build/build_config.h"
6
viettrungluu@chromium.org00155942013-01-26 06:51:35 +09007#if defined(OS_POSIX)
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09008#if defined(OS_MACOSX)
9extern "C" {
10#include <sandbox.h>
11}
12#endif
13#include <fcntl.h>
hubbe@chromium.org683920d2013-10-15 09:07:00 +090014#include <sys/socket.h>
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090015#include <sys/stat.h>
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090016#include <unistd.h>
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090017
hubbe@chromium.org683920d2013-10-15 09:07:00 +090018#include <queue>
19
20#include "base/callback.h"
erg@google.come6ffcb52010-08-18 03:38:24 +090021#include "base/file_descriptor_posix.h"
avi@chromium.orga29af562013-07-18 08:00:30 +090022#include "base/message_loop/message_loop.h"
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090023#include "base/pickle.h"
24#include "base/posix/eintr_wrapper.h"
hubbe@chromium.org683920d2013-10-15 09:07:00 +090025#include "base/synchronization/waitable_event.h"
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090026#include "ipc/ipc_message_utils.h"
27#include "ipc/ipc_test_base.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090028
29namespace {
30
31const unsigned kNumFDsToSend = 20;
32const char* kDevZeroPath = "/dev/zero";
33
hubbe@chromium.org683920d2013-10-15 09:07:00 +090034class MyChannelDescriptorListenerBase : public IPC::Listener {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090035 public:
rsleevi@chromium.org5a8d7ef2013-02-06 19:08:55 +090036 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
jbates@chromium.org0fc87362012-03-08 05:42:56 +090037 PickleIterator iter(message);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090038
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090039 base::FileDescriptor descriptor;
40
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090041 IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090042
hubbe@chromium.org683920d2013-10-15 09:07:00 +090043 HandleFD(descriptor.fd);
jam@chromium.org8a2c7842010-12-24 15:19:28 +090044 return true;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090045 }
46
hubbe@chromium.org683920d2013-10-15 09:07:00 +090047 protected:
48 virtual void HandleFD(int fd) = 0;
49};
50
51class 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) {
hubbe@chromium.org1b503fc2013-10-11 06:12:14 +090057 }
58
benwells@chromium.org6ecd7122013-10-11 13:22:34 +090059 bool GotExpectedNumberOfDescriptors() const {
60 return num_fds_received_ == kNumFDsToSend;
hubbe@chromium.org1b503fc2013-10-11 06:12:14 +090061 }
62
hubbe@chromium.org683920d2013-10-15 09:07:00 +090063 virtual void OnChannelError() OVERRIDE {
64 base::MessageLoop::current()->Quit();
65 }
66
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.
74
75 struct stat st;
76 ASSERT_EQ(fstat(fd, &st), 0);
77
78 ASSERT_EQ(close(fd), 0);
79
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);
83
84 ++num_fds_received_;
85 if (num_fds_received_ == kNumFDsToSend)
86 base::MessageLoop::current()->Quit();
87 }
88
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090089 private:
90 ino_t expected_inode_num_;
91 unsigned num_fds_received_;
92};
93
hubbe@chromium.org683920d2013-10-15 09:07:00 +090094
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090095class 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());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900103
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900104 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);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900108
bbudge@chromium.orgab4c6bc2013-11-05 07:28:12 +0900109 IPC::Message* message =
110 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900111 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
112 ASSERT_TRUE(sender()->Send(message));
113 }
114
115 // Run message loop.
xhwang@chromium.org0b2c2a52013-05-01 05:55:03 +0900116 base::MessageLoop::current()->Run();
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900117
118 // Close the channel so the client's OnChannelError() gets fired.
119 channel()->Close();
120
121 EXPECT_TRUE(WaitForClientShutdown());
122 DestroyChannel();
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900123 }
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900124};
125
126TEST_F(IPCSendFdsTest, DescriptorTest) {
127 Init("SendFdsClient");
128 RunServer();
129}
130
131int SendFdsClientCommon(const std::string& test_client_name,
132 ino_t expected_inode_num) {
xhwang@chromium.org0b2c2a52013-05-01 05:55:03 +0900133 base::MessageLoopForIO main_message_loop;
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900134 MyChannelDescriptorListener listener(expected_inode_num);
135
136 // Set up IPC channel.
morrita@chromium.org2ced0042014-05-30 12:58:59 +0900137 scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient(
138 IPCTestBase::GetChannelName(test_client_name),
139 &listener));
140 CHECK(channel->Connect());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900141
142 // Run message loop.
xhwang@chromium.org0b2c2a52013-05-01 05:55:03 +0900143 base::MessageLoop::current()->Run();
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900144
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900145 // Verify that the message loop was exited due to getting the correct number
146 // of descriptors, and not because of the channel closing unexpectedly.
dmaclach@chromium.org63b5df72010-12-09 10:12:20 +0900147 CHECK(listener.GotExpectedNumberOfDescriptors());
148
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900149 return 0;
150}
151
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900152MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
153 struct stat st;
154 int fd = open(kDevZeroPath, O_RDONLY);
155 fstat(fd, &st);
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900156 EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900157 return SendFdsClientCommon("SendFdsClient", st.st_ino);
158}
viettrungluu@chromium.org7d86af22013-01-12 00:13:37 +0900159
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900160#if defined(OS_MACOSX)
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900161// Test that FDs are correctly sent to a sandboxed process.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900162// TODO(port): Make this test cross-platform.
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900163TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
164 Init("SendFdsSandboxedClient");
165 RunServer();
166}
167
168MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900169 struct stat st;
170 const int fd = open(kDevZeroPath, O_RDONLY);
171 fstat(fd, &st);
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900172 if (IGNORE_EINTR(close(fd)) < 0)
thakis@chromium.org5dab7482010-10-04 09:34:04 +0900173 return -1;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900174
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900175 // Enable the sandbox.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900176 char* error_buff = NULL;
177 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
178 &error_buff);
179 bool success = (error == 0 && error_buff == NULL);
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900180 if (!success)
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900181 return -1;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900182
183 sandbox_free_error(error_buff);
184
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900185 // Make sure sandbox is really enabled.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900186 if (open(kDevZeroPath, O_RDONLY) != -1) {
187 LOG(ERROR) << "Sandbox wasn't properly enabled";
188 return -1;
189 }
190
191 // See if we can receive a file descriptor.
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900192 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900193}
194#endif // defined(OS_MACOSX)
195
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900196
197class MyCBListener : public MyChannelDescriptorListenerBase {
198 public:
199 MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
200 : MyChannelDescriptorListenerBase(),
201 cb_(cb) {
202 }
203
204 protected:
205 virtual void HandleFD(int fd) OVERRIDE {
206 cb_.Run(fd);
207 }
208 private:
209 base::Callback<void(int)> cb_;
210};
211
212std::pair<int, int> make_socket_pair() {
213 int pipe_fds[2];
214 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
215 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
216}
217
218static void null_cb(int unused_fd) {
219 NOTREACHED();
220}
221
222class PipeChannelHelper {
223 public:
224 PipeChannelHelper(base::Thread* in_thread,
225 base::Thread* out_thread,
226 base::Callback<void(int)> cb,
227 int fds_to_send) :
228 in_thread_(in_thread),
229 out_thread_(out_thread),
230 cb_listener_(cb, fds_to_send),
231 null_listener_(base::Bind(&null_cb), 0) {
232 }
233
234 void Init() {
235 IPC::ChannelHandle in_handle("IN");
morrita@chromium.org2ced0042014-05-30 12:58:59 +0900236 in = IPC::Channel::CreateServer(in_handle, &null_listener_);
morrita@chromium.orgfde2b6b2014-06-07 05:13:51 +0900237 base::FileDescriptor out_fd(
238 in->TakeClientFileDescriptor(), false);
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900239 IPC::ChannelHandle out_handle("OUT", out_fd);
morrita@chromium.org2ced0042014-05-30 12:58:59 +0900240 out = IPC::Channel::CreateClient(out_handle, &cb_listener_);
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900241 // PostTask the connect calls to make sure the callbacks happens
242 // on the right threads.
243 in_thread_->message_loop()->PostTask(
244 FROM_HERE,
245 base::Bind(&PipeChannelHelper::Connect, in.get()));
246 out_thread_->message_loop()->PostTask(
247 FROM_HERE,
248 base::Bind(&PipeChannelHelper::Connect, out.get()));
249 }
250
251 static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
252 base::WaitableEvent *event) {
253 c->reset(0);
254 event->Signal();
255 }
256
257 ~PipeChannelHelper() {
258 base::WaitableEvent a(true, false);
259 base::WaitableEvent b(true, false);
260 in_thread_->message_loop()->PostTask(
261 FROM_HERE,
262 base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
263 out_thread_->message_loop()->PostTask(
264 FROM_HERE,
265 base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
266 a.Wait();
267 b.Wait();
268 }
269
270 static void Connect(IPC::Channel *channel) {
271 EXPECT_TRUE(channel->Connect());
272 }
273
274 void Send(int fd) {
275 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
276
277 ASSERT_GE(fd, 0);
278 base::FileDescriptor descriptor(fd, true);
279
bbudge@chromium.orgab4c6bc2013-11-05 07:28:12 +0900280 IPC::Message* message =
281 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900282 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
283 ASSERT_TRUE(in->Send(message));
284 }
285
286 private:
287 scoped_ptr<IPC::Channel> in, out;
288 base::Thread* in_thread_;
289 base::Thread* out_thread_;
290 MyCBListener cb_listener_;
291 MyCBListener null_listener_;
292};
293
294// This test is meant to provoke a kernel bug on OSX, and to prove
295// that the workaround for it is working. It sets up two pipes and three
296// threads, the producer thread creates socketpairs and sends one of the fds
297// over pipe1 to the middleman thread. The middleman thread simply takes the fd
298// sends it over pipe2 to the consumer thread. The consumer thread writes a byte
299// to each fd it receives and then closes the pipe. The producer thread reads
300// the bytes back from each pair of pipes and make sure that everything worked.
301// This feedback mechanism makes sure that not too many file descriptors are
302// in flight at the same time. For more info on the bug, see:
303// http://crbug.com/298276
304class IPCMultiSendingFdsTest : public testing::Test {
305 public:
306 IPCMultiSendingFdsTest() : received_(true, false) {}
307
308 void Producer(PipeChannelHelper* dest,
309 base::Thread* t,
310 int pipes_to_send) {
311 for (int i = 0; i < pipes_to_send; i++) {
312 received_.Reset();
313 std::pair<int, int> pipe_fds = make_socket_pair();
314 t->message_loop()->PostTask(
315 FROM_HERE,
316 base::Bind(&PipeChannelHelper::Send,
317 base::Unretained(dest),
318 pipe_fds.second));
319 char tmp = 'x';
320 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900321 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first)));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900322 received_.Wait();
323 }
324 }
325
326 void ConsumerHandleFD(int fd) {
327 char tmp = 'y';
328 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
329 CHECK_EQ(tmp, 'x');
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900330 CHECK_EQ(0, IGNORE_EINTR(close(fd)));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900331 received_.Signal();
332 }
333
334 base::Thread* CreateThread(const char* name) {
335 base::Thread* ret = new base::Thread(name);
336 base::Thread::Options options;
337 options.message_loop_type = base::MessageLoop::TYPE_IO;
338 ret->StartWithOptions(options);
339 return ret;
340 }
341
342 void Run() {
343 // On my mac, this test fails roughly 35 times per
344 // million sends with low load, but much more with high load.
345 // Unless the workaround is in place. With 10000 sends, we
346 // should see at least a 3% failure rate.
347 const int pipes_to_send = 20000;
348 scoped_ptr<base::Thread> producer(CreateThread("producer"));
349 scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
350 scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
351 PipeChannelHelper pipe1(
352 middleman.get(),
353 consumer.get(),
354 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
355 base::Unretained(this)),
356 pipes_to_send);
357 PipeChannelHelper pipe2(
358 producer.get(),
359 middleman.get(),
360 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
361 pipes_to_send);
362 pipe1.Init();
363 pipe2.Init();
364 Producer(&pipe2, producer.get(), pipes_to_send);
365 }
366
367 private:
368 base::WaitableEvent received_;
369};
370
371TEST_F(IPCMultiSendingFdsTest, StressTest) {
372 Run();
373}
374
viettrungluu@chromium.org7ca19132013-01-12 05:56:22 +0900375} // namespace
376
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900377#endif // defined(OS_POSIX)