blob: 033ae397e41ecbcef17870b9b5a6593f344b9abe [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>
kerrnelf8e810e2016-04-13 01:39:06 +090011};
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090012#endif
13#include <fcntl.h>
avi42ebda42015-12-22 11:39:04 +090014#include <stddef.h>
hubbe@chromium.org683920d2013-10-15 09:07:00 +090015#include <sys/socket.h>
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090016#include <sys/stat.h>
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090017#include <unistd.h>
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090018
danakjc3fb6c52016-04-23 13:21:09 +090019#include <memory>
hubbe@chromium.org683920d2013-10-15 09:07:00 +090020#include <queue>
21
22#include "base/callback.h"
erg@google.come6ffcb52010-08-18 03:38:24 +090023#include "base/file_descriptor_posix.h"
skyostile468e662015-05-12 20:29:21 +090024#include "base/location.h"
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090025#include "base/pickle.h"
26#include "base/posix/eintr_wrapper.h"
fdoray284aae52016-06-23 04:56:16 +090027#include "base/run_loop.h"
skyostile468e662015-05-12 20:29:21 +090028#include "base/single_thread_task_runner.h"
hubbe@chromium.org683920d2013-10-15 09:07:00 +090029#include "base/synchronization/waitable_event.h"
rockot37e7fa42016-07-20 13:28:32 +090030#include "base/threading/thread_task_runner_handle.h"
morrita33a35902015-01-15 06:17:06 +090031#include "ipc/ipc_message_attachment_set.h"
viettrungluu@chromium.org00155942013-01-26 06:51:35 +090032#include "ipc/ipc_message_utils.h"
33#include "ipc/ipc_test_base.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090034
yusukesf8a50112015-01-06 15:56:34 +090035#if defined(OS_POSIX)
36#include "base/macros.h"
yusukesf8a50112015-01-06 15:56:34 +090037#endif
38
kerrnelf8e810e2016-04-13 01:39:06 +090039#if defined(OS_MACOSX)
40#include "sandbox/mac/seatbelt.h"
41#endif
42
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090043namespace {
44
yusukesdf3387d2015-05-07 04:45:45 +090045const unsigned kNumFDsToSend = 7; // per message
46const unsigned kNumMessages = 20;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090047const char* kDevZeroPath = "/dev/zero";
48
yusukesf8a50112015-01-06 15:56:34 +090049#if defined(OS_POSIX)
anujk.sharmaf56be5d2015-01-22 14:39:37 +090050static_assert(kNumFDsToSend ==
51 IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage,
52 "The number of FDs to send must be kMaxDescriptorsPerMessage.");
yusukesf8a50112015-01-06 15:56:34 +090053#endif
54
hubbe@chromium.org683920d2013-10-15 09:07:00 +090055class MyChannelDescriptorListenerBase : public IPC::Listener {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090056 public:
dchengef7721a2014-10-22 11:29:52 +090057 bool OnMessageReceived(const IPC::Message& message) override {
brettwf3146202015-06-03 13:29:25 +090058 base::PickleIterator iter(message);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090059 base::FileDescriptor descriptor;
yusukesf8a50112015-01-06 15:56:34 +090060 while (IPC::ParamTraits<base::FileDescriptor>::Read(
61 &message, &iter, &descriptor)) {
62 HandleFD(descriptor.fd);
63 }
jam@chromium.org8a2c7842010-12-24 15:19:28 +090064 return true;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090065 }
66
hubbe@chromium.org683920d2013-10-15 09:07:00 +090067 protected:
68 virtual void HandleFD(int fd) = 0;
69};
70
71class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
72 public:
73 explicit MyChannelDescriptorListener(ino_t expected_inode_num)
74 : MyChannelDescriptorListenerBase(),
75 expected_inode_num_(expected_inode_num),
76 num_fds_received_(0) {
hubbe@chromium.org1b503fc2013-10-11 06:12:14 +090077 }
78
benwells@chromium.org6ecd7122013-10-11 13:22:34 +090079 bool GotExpectedNumberOfDescriptors() const {
yusukesf8a50112015-01-06 15:56:34 +090080 return num_fds_received_ == kNumFDsToSend * kNumMessages;
hubbe@chromium.org1b503fc2013-10-11 06:12:14 +090081 }
82
dchengef7721a2014-10-22 11:29:52 +090083 void OnChannelError() override {
ki.stfuad029642015-10-13 02:26:00 +090084 base::MessageLoop::current()->QuitWhenIdle();
dcheng9b01d242014-10-22 03:02:42 +090085 }
hubbe@chromium.org683920d2013-10-15 09:07:00 +090086
87 protected:
dchengef7721a2014-10-22 11:29:52 +090088 void HandleFD(int fd) override {
yusukesf8a50112015-01-06 15:56:34 +090089 ASSERT_GE(fd, 0);
hubbe@chromium.org683920d2013-10-15 09:07:00 +090090 // Check that we can read from the FD.
91 char buf;
92 ssize_t amt_read = read(fd, &buf, 1);
93 ASSERT_EQ(amt_read, 1);
94 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
95
96 struct stat st;
97 ASSERT_EQ(fstat(fd, &st), 0);
98
99 ASSERT_EQ(close(fd), 0);
100
101 // Compare inode numbers to check that the file sent over the wire is
102 // actually the one expected.
103 ASSERT_EQ(expected_inode_num_, st.st_ino);
104
105 ++num_fds_received_;
yusukesf8a50112015-01-06 15:56:34 +0900106 if (num_fds_received_ == kNumFDsToSend * kNumMessages)
ki.stfuad029642015-10-13 02:26:00 +0900107 base::MessageLoop::current()->QuitWhenIdle();
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900108 }
109
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900110 private:
111 ino_t expected_inode_num_;
112 unsigned num_fds_received_;
113};
114
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900115
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900116class IPCSendFdsTest : public IPCTestBase {
117 protected:
118 void RunServer() {
119 // Set up IPC channel and start client.
120 MyChannelDescriptorListener listener(-1);
121 CreateChannel(&listener);
122 ASSERT_TRUE(ConnectChannel());
123 ASSERT_TRUE(StartClient());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900124
yusukesf8a50112015-01-06 15:56:34 +0900125 for (unsigned i = 0; i < kNumMessages; ++i) {
bbudge@chromium.orgab4c6bc2013-11-05 07:28:12 +0900126 IPC::Message* message =
127 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
yusukesf8a50112015-01-06 15:56:34 +0900128 for (unsigned j = 0; j < kNumFDsToSend; ++j) {
129 const int fd = open(kDevZeroPath, O_RDONLY);
130 ASSERT_GE(fd, 0);
131 base::FileDescriptor descriptor(fd, true);
132 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
133 }
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900134 ASSERT_TRUE(sender()->Send(message));
135 }
136
137 // Run message loop.
fdoray284aae52016-06-23 04:56:16 +0900138 base::RunLoop().Run();
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900139
140 // Close the channel so the client's OnChannelError() gets fired.
141 channel()->Close();
142
143 EXPECT_TRUE(WaitForClientShutdown());
144 DestroyChannel();
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900145 }
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900146};
147
amistry87fc78f2016-05-05 14:12:09 +0900148TEST_F(IPCSendFdsTest, DescriptorTest) {
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900149 Init("SendFdsClient");
150 RunServer();
151}
152
153int SendFdsClientCommon(const std::string& test_client_name,
154 ino_t expected_inode_num) {
xhwang@chromium.org0b2c2a52013-05-01 05:55:03 +0900155 base::MessageLoopForIO main_message_loop;
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900156 MyChannelDescriptorListener listener(expected_inode_num);
157
158 // Set up IPC channel.
danakjc3fb6c52016-04-23 13:21:09 +0900159 std::unique_ptr<IPC::Channel> channel(IPC::Channel::CreateClient(
rockot37e7fa42016-07-20 13:28:32 +0900160 IPCTestBase::GetChannelName(test_client_name), &listener,
161 main_message_loop.task_runner()));
morrita@chromium.org2ced0042014-05-30 12:58:59 +0900162 CHECK(channel->Connect());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900163
164 // Run message loop.
fdoray284aae52016-06-23 04:56:16 +0900165 base::RunLoop().Run();
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900166
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900167 // Verify that the message loop was exited due to getting the correct number
168 // of descriptors, and not because of the channel closing unexpectedly.
dmaclach@chromium.org63b5df72010-12-09 10:12:20 +0900169 CHECK(listener.GotExpectedNumberOfDescriptors());
170
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900171 return 0;
172}
173
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900174MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
175 struct stat st;
176 int fd = open(kDevZeroPath, O_RDONLY);
177 fstat(fd, &st);
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900178 EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900179 return SendFdsClientCommon("SendFdsClient", st.st_ino);
180}
viettrungluu@chromium.org7d86af22013-01-12 00:13:37 +0900181
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900182#if defined(OS_MACOSX)
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900183// Test that FDs are correctly sent to a sandboxed process.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900184// TODO(port): Make this test cross-platform.
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900185TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
186 Init("SendFdsSandboxedClient");
187 RunServer();
188}
189
190MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900191 struct stat st;
192 const int fd = open(kDevZeroPath, O_RDONLY);
193 fstat(fd, &st);
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900194 if (IGNORE_EINTR(close(fd)) < 0)
thakis@chromium.org5dab7482010-10-04 09:34:04 +0900195 return -1;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900196
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900197 // Enable the sandbox.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900198 char* error_buff = NULL;
kerrnelf8e810e2016-04-13 01:39:06 +0900199 int error = sandbox::Seatbelt::Init(kSBXProfilePureComputation, SANDBOX_NAMED,
200 &error_buff);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900201 bool success = (error == 0 && error_buff == NULL);
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900202 if (!success)
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900203 return -1;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900204
kerrnelf8e810e2016-04-13 01:39:06 +0900205 sandbox::Seatbelt::FreeError(error_buff);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900206
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900207 // Make sure sandbox is really enabled.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900208 if (open(kDevZeroPath, O_RDONLY) != -1) {
209 LOG(ERROR) << "Sandbox wasn't properly enabled";
210 return -1;
211 }
212
213 // See if we can receive a file descriptor.
viettrungluu@chromium.org00155942013-01-26 06:51:35 +0900214 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900215}
216#endif // defined(OS_MACOSX)
217
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900218
219class MyCBListener : public MyChannelDescriptorListenerBase {
220 public:
221 MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
222 : MyChannelDescriptorListenerBase(),
223 cb_(cb) {
224 }
225
226 protected:
dchengef7721a2014-10-22 11:29:52 +0900227 void HandleFD(int fd) override { cb_.Run(fd); }
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900228 private:
229 base::Callback<void(int)> cb_;
230};
231
232std::pair<int, int> make_socket_pair() {
233 int pipe_fds[2];
234 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
235 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
236}
237
238static void null_cb(int unused_fd) {
239 NOTREACHED();
240}
241
242class PipeChannelHelper {
243 public:
244 PipeChannelHelper(base::Thread* in_thread,
245 base::Thread* out_thread,
246 base::Callback<void(int)> cb,
247 int fds_to_send) :
248 in_thread_(in_thread),
249 out_thread_(out_thread),
250 cb_listener_(cb, fds_to_send),
251 null_listener_(base::Bind(&null_cb), 0) {
252 }
253
254 void Init() {
255 IPC::ChannelHandle in_handle("IN");
rockot37e7fa42016-07-20 13:28:32 +0900256 in = IPC::Channel::CreateServer(
257 in_handle, &null_listener_, in_thread_->task_runner());
morrita13e514d2014-10-21 08:53:25 +0900258 IPC::ChannelHandle out_handle(
259 "OUT", base::FileDescriptor(in->TakeClientFileDescriptor()));
rockot37e7fa42016-07-20 13:28:32 +0900260 out = IPC::Channel::CreateClient(
261 out_handle, &cb_listener_, out_thread_->task_runner());
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900262 // PostTask the connect calls to make sure the callbacks happens
263 // on the right threads.
skyostile468e662015-05-12 20:29:21 +0900264 in_thread_->task_runner()->PostTask(
265 FROM_HERE, base::Bind(&PipeChannelHelper::Connect, in.get()));
266 out_thread_->task_runner()->PostTask(
267 FROM_HERE, base::Bind(&PipeChannelHelper::Connect, out.get()));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900268 }
269
danakjc3fb6c52016-04-23 13:21:09 +0900270 static void DestroyChannel(std::unique_ptr<IPC::Channel>* c,
271 base::WaitableEvent* event) {
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900272 c->reset(0);
273 event->Signal();
274 }
275
276 ~PipeChannelHelper() {
gabc1aebcf2016-06-02 05:34:06 +0900277 base::WaitableEvent a(base::WaitableEvent::ResetPolicy::MANUAL,
278 base::WaitableEvent::InitialState::NOT_SIGNALED);
279 base::WaitableEvent b(base::WaitableEvent::ResetPolicy::MANUAL,
280 base::WaitableEvent::InitialState::NOT_SIGNALED);
skyostile468e662015-05-12 20:29:21 +0900281 in_thread_->task_runner()->PostTask(
282 FROM_HERE, base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
283 out_thread_->task_runner()->PostTask(
284 FROM_HERE, base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900285 a.Wait();
286 b.Wait();
287 }
288
289 static void Connect(IPC::Channel *channel) {
290 EXPECT_TRUE(channel->Connect());
291 }
292
293 void Send(int fd) {
294 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
295
296 ASSERT_GE(fd, 0);
297 base::FileDescriptor descriptor(fd, true);
298
bbudge@chromium.orgab4c6bc2013-11-05 07:28:12 +0900299 IPC::Message* message =
300 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900301 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
302 ASSERT_TRUE(in->Send(message));
303 }
304
305 private:
danakjc3fb6c52016-04-23 13:21:09 +0900306 std::unique_ptr<IPC::Channel> in, out;
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900307 base::Thread* in_thread_;
308 base::Thread* out_thread_;
309 MyCBListener cb_listener_;
310 MyCBListener null_listener_;
311};
312
313// This test is meant to provoke a kernel bug on OSX, and to prove
314// that the workaround for it is working. It sets up two pipes and three
315// threads, the producer thread creates socketpairs and sends one of the fds
316// over pipe1 to the middleman thread. The middleman thread simply takes the fd
317// sends it over pipe2 to the consumer thread. The consumer thread writes a byte
318// to each fd it receives and then closes the pipe. The producer thread reads
319// the bytes back from each pair of pipes and make sure that everything worked.
320// This feedback mechanism makes sure that not too many file descriptors are
321// in flight at the same time. For more info on the bug, see:
322// http://crbug.com/298276
323class IPCMultiSendingFdsTest : public testing::Test {
324 public:
gabc1aebcf2016-06-02 05:34:06 +0900325 IPCMultiSendingFdsTest()
326 : received_(base::WaitableEvent::ResetPolicy::MANUAL,
327 base::WaitableEvent::InitialState::NOT_SIGNALED) {}
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900328
329 void Producer(PipeChannelHelper* dest,
330 base::Thread* t,
331 int pipes_to_send) {
332 for (int i = 0; i < pipes_to_send; i++) {
333 received_.Reset();
334 std::pair<int, int> pipe_fds = make_socket_pair();
skyostile468e662015-05-12 20:29:21 +0900335 t->task_runner()->PostTask(
336 FROM_HERE, base::Bind(&PipeChannelHelper::Send,
337 base::Unretained(dest), pipe_fds.second));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900338 char tmp = 'x';
339 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900340 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first)));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900341 received_.Wait();
342 }
343 }
344
345 void ConsumerHandleFD(int fd) {
346 char tmp = 'y';
347 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
348 CHECK_EQ(tmp, 'x');
mark@chromium.orgfa5a0f92013-12-03 23:10:59 +0900349 CHECK_EQ(0, IGNORE_EINTR(close(fd)));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900350 received_.Signal();
351 }
352
353 base::Thread* CreateThread(const char* name) {
354 base::Thread* ret = new base::Thread(name);
355 base::Thread::Options options;
356 options.message_loop_type = base::MessageLoop::TYPE_IO;
357 ret->StartWithOptions(options);
358 return ret;
359 }
360
361 void Run() {
362 // On my mac, this test fails roughly 35 times per
363 // million sends with low load, but much more with high load.
364 // Unless the workaround is in place. With 10000 sends, we
365 // should see at least a 3% failure rate.
366 const int pipes_to_send = 20000;
danakjc3fb6c52016-04-23 13:21:09 +0900367 std::unique_ptr<base::Thread> producer(CreateThread("producer"));
368 std::unique_ptr<base::Thread> middleman(CreateThread("middleman"));
369 std::unique_ptr<base::Thread> consumer(CreateThread("consumer"));
hubbe@chromium.org683920d2013-10-15 09:07:00 +0900370 PipeChannelHelper pipe1(
371 middleman.get(),
372 consumer.get(),
373 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
374 base::Unretained(this)),
375 pipes_to_send);
376 PipeChannelHelper pipe2(
377 producer.get(),
378 middleman.get(),
379 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
380 pipes_to_send);
381 pipe1.Init();
382 pipe2.Init();
383 Producer(&pipe2, producer.get(), pipes_to_send);
384 }
385
386 private:
387 base::WaitableEvent received_;
388};
389
390TEST_F(IPCMultiSendingFdsTest, StressTest) {
391 Run();
392}
393
viettrungluu@chromium.org7ca19132013-01-12 05:56:22 +0900394} // namespace
395
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900396#endif // defined(OS_POSIX)