Extract LoginPerformer to chromeos/auth
[chromium-blink-merge.git] / ipc / ipc_send_fds_test.cc
blob332a631068e55e2a355d0c60a7999041afe23ae8
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 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 void OnChannelError() override { base::MessageLoop::current()->Quit(); }
65 protected:
66 void HandleFD(int fd) override {
67 // Check that we can read from the FD.
68 char buf;
69 ssize_t amt_read = read(fd, &buf, 1);
70 ASSERT_EQ(amt_read, 1);
71 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
73 struct stat st;
74 ASSERT_EQ(fstat(fd, &st), 0);
76 ASSERT_EQ(close(fd), 0);
78 // Compare inode numbers to check that the file sent over the wire is
79 // actually the one expected.
80 ASSERT_EQ(expected_inode_num_, st.st_ino);
82 ++num_fds_received_;
83 if (num_fds_received_ == kNumFDsToSend)
84 base::MessageLoop::current()->Quit();
87 private:
88 ino_t expected_inode_num_;
89 unsigned num_fds_received_;
93 class IPCSendFdsTest : public IPCTestBase {
94 protected:
95 void RunServer() {
96 // Set up IPC channel and start client.
97 MyChannelDescriptorListener listener(-1);
98 CreateChannel(&listener);
99 ASSERT_TRUE(ConnectChannel());
100 ASSERT_TRUE(StartClient());
102 for (unsigned i = 0; i < kNumFDsToSend; ++i) {
103 const int fd = open(kDevZeroPath, O_RDONLY);
104 ASSERT_GE(fd, 0);
105 base::FileDescriptor descriptor(fd, true);
107 IPC::Message* message =
108 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
109 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
110 ASSERT_TRUE(sender()->Send(message));
113 // Run message loop.
114 base::MessageLoop::current()->Run();
116 // Close the channel so the client's OnChannelError() gets fired.
117 channel()->Close();
119 EXPECT_TRUE(WaitForClientShutdown());
120 DestroyChannel();
124 TEST_F(IPCSendFdsTest, DescriptorTest) {
125 Init("SendFdsClient");
126 RunServer();
129 int SendFdsClientCommon(const std::string& test_client_name,
130 ino_t expected_inode_num) {
131 base::MessageLoopForIO main_message_loop;
132 MyChannelDescriptorListener listener(expected_inode_num);
134 // Set up IPC channel.
135 scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient(
136 IPCTestBase::GetChannelName(test_client_name),
137 &listener));
138 CHECK(channel->Connect());
140 // Run message loop.
141 base::MessageLoop::current()->Run();
143 // Verify that the message loop was exited due to getting the correct number
144 // of descriptors, and not because of the channel closing unexpectedly.
145 CHECK(listener.GotExpectedNumberOfDescriptors());
147 return 0;
150 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
151 struct stat st;
152 int fd = open(kDevZeroPath, O_RDONLY);
153 fstat(fd, &st);
154 EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
155 return SendFdsClientCommon("SendFdsClient", st.st_ino);
158 #if defined(OS_MACOSX)
159 // Test that FDs are correctly sent to a sandboxed process.
160 // TODO(port): Make this test cross-platform.
161 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
162 Init("SendFdsSandboxedClient");
163 RunServer();
166 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
167 struct stat st;
168 const int fd = open(kDevZeroPath, O_RDONLY);
169 fstat(fd, &st);
170 if (IGNORE_EINTR(close(fd)) < 0)
171 return -1;
173 // Enable the sandbox.
174 char* error_buff = NULL;
175 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
176 &error_buff);
177 bool success = (error == 0 && error_buff == NULL);
178 if (!success)
179 return -1;
181 sandbox_free_error(error_buff);
183 // Make sure sandbox is really enabled.
184 if (open(kDevZeroPath, O_RDONLY) != -1) {
185 LOG(ERROR) << "Sandbox wasn't properly enabled";
186 return -1;
189 // See if we can receive a file descriptor.
190 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
192 #endif // defined(OS_MACOSX)
195 class MyCBListener : public MyChannelDescriptorListenerBase {
196 public:
197 MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
198 : MyChannelDescriptorListenerBase(),
199 cb_(cb) {
202 protected:
203 void HandleFD(int fd) override { cb_.Run(fd); }
204 private:
205 base::Callback<void(int)> cb_;
208 std::pair<int, int> make_socket_pair() {
209 int pipe_fds[2];
210 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
211 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
214 static void null_cb(int unused_fd) {
215 NOTREACHED();
218 class PipeChannelHelper {
219 public:
220 PipeChannelHelper(base::Thread* in_thread,
221 base::Thread* out_thread,
222 base::Callback<void(int)> cb,
223 int fds_to_send) :
224 in_thread_(in_thread),
225 out_thread_(out_thread),
226 cb_listener_(cb, fds_to_send),
227 null_listener_(base::Bind(&null_cb), 0) {
230 void Init() {
231 IPC::ChannelHandle in_handle("IN");
232 in = IPC::Channel::CreateServer(in_handle, &null_listener_);
233 IPC::ChannelHandle out_handle(
234 "OUT", base::FileDescriptor(in->TakeClientFileDescriptor()));
235 out = IPC::Channel::CreateClient(out_handle, &cb_listener_);
236 // PostTask the connect calls to make sure the callbacks happens
237 // on the right threads.
238 in_thread_->message_loop()->PostTask(
239 FROM_HERE,
240 base::Bind(&PipeChannelHelper::Connect, in.get()));
241 out_thread_->message_loop()->PostTask(
242 FROM_HERE,
243 base::Bind(&PipeChannelHelper::Connect, out.get()));
246 static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
247 base::WaitableEvent *event) {
248 c->reset(0);
249 event->Signal();
252 ~PipeChannelHelper() {
253 base::WaitableEvent a(true, false);
254 base::WaitableEvent b(true, false);
255 in_thread_->message_loop()->PostTask(
256 FROM_HERE,
257 base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
258 out_thread_->message_loop()->PostTask(
259 FROM_HERE,
260 base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
261 a.Wait();
262 b.Wait();
265 static void Connect(IPC::Channel *channel) {
266 EXPECT_TRUE(channel->Connect());
269 void Send(int fd) {
270 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
272 ASSERT_GE(fd, 0);
273 base::FileDescriptor descriptor(fd, true);
275 IPC::Message* message =
276 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
277 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
278 ASSERT_TRUE(in->Send(message));
281 private:
282 scoped_ptr<IPC::Channel> in, out;
283 base::Thread* in_thread_;
284 base::Thread* out_thread_;
285 MyCBListener cb_listener_;
286 MyCBListener null_listener_;
289 // This test is meant to provoke a kernel bug on OSX, and to prove
290 // that the workaround for it is working. It sets up two pipes and three
291 // threads, the producer thread creates socketpairs and sends one of the fds
292 // over pipe1 to the middleman thread. The middleman thread simply takes the fd
293 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte
294 // to each fd it receives and then closes the pipe. The producer thread reads
295 // the bytes back from each pair of pipes and make sure that everything worked.
296 // This feedback mechanism makes sure that not too many file descriptors are
297 // in flight at the same time. For more info on the bug, see:
298 // http://crbug.com/298276
299 class IPCMultiSendingFdsTest : public testing::Test {
300 public:
301 IPCMultiSendingFdsTest() : received_(true, false) {}
303 void Producer(PipeChannelHelper* dest,
304 base::Thread* t,
305 int pipes_to_send) {
306 for (int i = 0; i < pipes_to_send; i++) {
307 received_.Reset();
308 std::pair<int, int> pipe_fds = make_socket_pair();
309 t->message_loop()->PostTask(
310 FROM_HERE,
311 base::Bind(&PipeChannelHelper::Send,
312 base::Unretained(dest),
313 pipe_fds.second));
314 char tmp = 'x';
315 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
316 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first)));
317 received_.Wait();
321 void ConsumerHandleFD(int fd) {
322 char tmp = 'y';
323 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
324 CHECK_EQ(tmp, 'x');
325 CHECK_EQ(0, IGNORE_EINTR(close(fd)));
326 received_.Signal();
329 base::Thread* CreateThread(const char* name) {
330 base::Thread* ret = new base::Thread(name);
331 base::Thread::Options options;
332 options.message_loop_type = base::MessageLoop::TYPE_IO;
333 ret->StartWithOptions(options);
334 return ret;
337 void Run() {
338 // On my mac, this test fails roughly 35 times per
339 // million sends with low load, but much more with high load.
340 // Unless the workaround is in place. With 10000 sends, we
341 // should see at least a 3% failure rate.
342 const int pipes_to_send = 20000;
343 scoped_ptr<base::Thread> producer(CreateThread("producer"));
344 scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
345 scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
346 PipeChannelHelper pipe1(
347 middleman.get(),
348 consumer.get(),
349 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
350 base::Unretained(this)),
351 pipes_to_send);
352 PipeChannelHelper pipe2(
353 producer.get(),
354 middleman.get(),
355 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
356 pipes_to_send);
357 pipe1.Init();
358 pipe2.Init();
359 Producer(&pipe2, producer.get(), pipes_to_send);
362 private:
363 base::WaitableEvent received_;
366 TEST_F(IPCMultiSendingFdsTest, StressTest) {
367 Run();
370 } // namespace
372 #endif // defined(OS_POSIX)