Enable Finch selection of Client Side Detection models.
[chromium-blink-merge.git] / ipc / ipc_send_fds_test.cc
blob2328c30022151f675ced6b649a1f7fee317253ac
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/location.h"
23 #include "base/pickle.h"
24 #include "base/posix/eintr_wrapper.h"
25 #include "base/single_thread_task_runner.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "ipc/ipc_message_attachment_set.h"
28 #include "ipc/ipc_message_utils.h"
29 #include "ipc/ipc_test_base.h"
31 #if defined(OS_POSIX)
32 #include "base/macros.h"
33 #endif
35 namespace {
37 const unsigned kNumFDsToSend = 7; // per message
38 const unsigned kNumMessages = 20;
39 const char* kDevZeroPath = "/dev/zero";
41 #if defined(OS_POSIX)
42 static_assert(kNumFDsToSend ==
43 IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage,
44 "The number of FDs to send must be kMaxDescriptorsPerMessage.");
45 #endif
47 class MyChannelDescriptorListenerBase : public IPC::Listener {
48 public:
49 bool OnMessageReceived(const IPC::Message& message) override {
50 base::PickleIterator iter(message);
51 base::FileDescriptor descriptor;
52 while (IPC::ParamTraits<base::FileDescriptor>::Read(
53 &message, &iter, &descriptor)) {
54 HandleFD(descriptor.fd);
56 return true;
59 protected:
60 virtual void HandleFD(int fd) = 0;
63 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
64 public:
65 explicit MyChannelDescriptorListener(ino_t expected_inode_num)
66 : MyChannelDescriptorListenerBase(),
67 expected_inode_num_(expected_inode_num),
68 num_fds_received_(0) {
71 bool GotExpectedNumberOfDescriptors() const {
72 return num_fds_received_ == kNumFDsToSend * kNumMessages;
75 void OnChannelError() override {
76 base::MessageLoop::current()->Quit();
79 protected:
80 void HandleFD(int fd) override {
81 ASSERT_GE(fd, 0);
82 // Check that we can read from the FD.
83 char buf;
84 ssize_t amt_read = read(fd, &buf, 1);
85 ASSERT_EQ(amt_read, 1);
86 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
88 struct stat st;
89 ASSERT_EQ(fstat(fd, &st), 0);
91 ASSERT_EQ(close(fd), 0);
93 // Compare inode numbers to check that the file sent over the wire is
94 // actually the one expected.
95 ASSERT_EQ(expected_inode_num_, st.st_ino);
97 ++num_fds_received_;
98 if (num_fds_received_ == kNumFDsToSend * kNumMessages)
99 base::MessageLoop::current()->Quit();
102 private:
103 ino_t expected_inode_num_;
104 unsigned num_fds_received_;
108 class IPCSendFdsTest : public IPCTestBase {
109 protected:
110 void RunServer() {
111 // Set up IPC channel and start client.
112 MyChannelDescriptorListener listener(-1);
113 CreateChannel(&listener);
114 ASSERT_TRUE(ConnectChannel());
115 ASSERT_TRUE(StartClient());
117 for (unsigned i = 0; i < kNumMessages; ++i) {
118 IPC::Message* message =
119 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
120 for (unsigned j = 0; j < kNumFDsToSend; ++j) {
121 const int fd = open(kDevZeroPath, O_RDONLY);
122 ASSERT_GE(fd, 0);
123 base::FileDescriptor descriptor(fd, true);
124 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
126 ASSERT_TRUE(sender()->Send(message));
129 // Run message loop.
130 base::MessageLoop::current()->Run();
132 // Close the channel so the client's OnChannelError() gets fired.
133 channel()->Close();
135 EXPECT_TRUE(WaitForClientShutdown());
136 DestroyChannel();
140 TEST_F(IPCSendFdsTest, DescriptorTest) {
141 Init("SendFdsClient");
142 RunServer();
145 int SendFdsClientCommon(const std::string& test_client_name,
146 ino_t expected_inode_num) {
147 base::MessageLoopForIO main_message_loop;
148 MyChannelDescriptorListener listener(expected_inode_num);
150 // Set up IPC channel.
151 scoped_ptr<IPC::Channel> channel(IPC::Channel::CreateClient(
152 IPCTestBase::GetChannelName(test_client_name),
153 &listener));
154 CHECK(channel->Connect());
156 // Run message loop.
157 base::MessageLoop::current()->Run();
159 // Verify that the message loop was exited due to getting the correct number
160 // of descriptors, and not because of the channel closing unexpectedly.
161 CHECK(listener.GotExpectedNumberOfDescriptors());
163 return 0;
166 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
167 struct stat st;
168 int fd = open(kDevZeroPath, O_RDONLY);
169 fstat(fd, &st);
170 EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
171 return SendFdsClientCommon("SendFdsClient", st.st_ino);
174 #if defined(OS_MACOSX)
175 // Test that FDs are correctly sent to a sandboxed process.
176 // TODO(port): Make this test cross-platform.
177 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
178 Init("SendFdsSandboxedClient");
179 RunServer();
182 MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
183 struct stat st;
184 const int fd = open(kDevZeroPath, O_RDONLY);
185 fstat(fd, &st);
186 if (IGNORE_EINTR(close(fd)) < 0)
187 return -1;
189 // Enable the sandbox.
190 char* error_buff = NULL;
191 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
192 &error_buff);
193 bool success = (error == 0 && error_buff == NULL);
194 if (!success)
195 return -1;
197 sandbox_free_error(error_buff);
199 // Make sure sandbox is really enabled.
200 if (open(kDevZeroPath, O_RDONLY) != -1) {
201 LOG(ERROR) << "Sandbox wasn't properly enabled";
202 return -1;
205 // See if we can receive a file descriptor.
206 return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
208 #endif // defined(OS_MACOSX)
211 class MyCBListener : public MyChannelDescriptorListenerBase {
212 public:
213 MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
214 : MyChannelDescriptorListenerBase(),
215 cb_(cb) {
218 protected:
219 void HandleFD(int fd) override { cb_.Run(fd); }
220 private:
221 base::Callback<void(int)> cb_;
224 std::pair<int, int> make_socket_pair() {
225 int pipe_fds[2];
226 CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
227 return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
230 static void null_cb(int unused_fd) {
231 NOTREACHED();
234 class PipeChannelHelper {
235 public:
236 PipeChannelHelper(base::Thread* in_thread,
237 base::Thread* out_thread,
238 base::Callback<void(int)> cb,
239 int fds_to_send) :
240 in_thread_(in_thread),
241 out_thread_(out_thread),
242 cb_listener_(cb, fds_to_send),
243 null_listener_(base::Bind(&null_cb), 0) {
246 void Init() {
247 IPC::ChannelHandle in_handle("IN");
248 in = IPC::Channel::CreateServer(in_handle, &null_listener_);
249 IPC::ChannelHandle out_handle(
250 "OUT", base::FileDescriptor(in->TakeClientFileDescriptor()));
251 out = IPC::Channel::CreateClient(out_handle, &cb_listener_);
252 // PostTask the connect calls to make sure the callbacks happens
253 // on the right threads.
254 in_thread_->task_runner()->PostTask(
255 FROM_HERE, base::Bind(&PipeChannelHelper::Connect, in.get()));
256 out_thread_->task_runner()->PostTask(
257 FROM_HERE, base::Bind(&PipeChannelHelper::Connect, out.get()));
260 static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
261 base::WaitableEvent *event) {
262 c->reset(0);
263 event->Signal();
266 ~PipeChannelHelper() {
267 base::WaitableEvent a(true, false);
268 base::WaitableEvent b(true, false);
269 in_thread_->task_runner()->PostTask(
270 FROM_HERE, base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
271 out_thread_->task_runner()->PostTask(
272 FROM_HERE, base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
273 a.Wait();
274 b.Wait();
277 static void Connect(IPC::Channel *channel) {
278 EXPECT_TRUE(channel->Connect());
281 void Send(int fd) {
282 CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
284 ASSERT_GE(fd, 0);
285 base::FileDescriptor descriptor(fd, true);
287 IPC::Message* message =
288 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
289 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
290 ASSERT_TRUE(in->Send(message));
293 private:
294 scoped_ptr<IPC::Channel> in, out;
295 base::Thread* in_thread_;
296 base::Thread* out_thread_;
297 MyCBListener cb_listener_;
298 MyCBListener null_listener_;
301 // This test is meant to provoke a kernel bug on OSX, and to prove
302 // that the workaround for it is working. It sets up two pipes and three
303 // threads, the producer thread creates socketpairs and sends one of the fds
304 // over pipe1 to the middleman thread. The middleman thread simply takes the fd
305 // sends it over pipe2 to the consumer thread. The consumer thread writes a byte
306 // to each fd it receives and then closes the pipe. The producer thread reads
307 // the bytes back from each pair of pipes and make sure that everything worked.
308 // This feedback mechanism makes sure that not too many file descriptors are
309 // in flight at the same time. For more info on the bug, see:
310 // http://crbug.com/298276
311 class IPCMultiSendingFdsTest : public testing::Test {
312 public:
313 IPCMultiSendingFdsTest() : received_(true, false) {}
315 void Producer(PipeChannelHelper* dest,
316 base::Thread* t,
317 int pipes_to_send) {
318 for (int i = 0; i < pipes_to_send; i++) {
319 received_.Reset();
320 std::pair<int, int> pipe_fds = make_socket_pair();
321 t->task_runner()->PostTask(
322 FROM_HERE, base::Bind(&PipeChannelHelper::Send,
323 base::Unretained(dest), pipe_fds.second));
324 char tmp = 'x';
325 CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
326 CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first)));
327 received_.Wait();
331 void ConsumerHandleFD(int fd) {
332 char tmp = 'y';
333 CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
334 CHECK_EQ(tmp, 'x');
335 CHECK_EQ(0, IGNORE_EINTR(close(fd)));
336 received_.Signal();
339 base::Thread* CreateThread(const char* name) {
340 base::Thread* ret = new base::Thread(name);
341 base::Thread::Options options;
342 options.message_loop_type = base::MessageLoop::TYPE_IO;
343 ret->StartWithOptions(options);
344 return ret;
347 void Run() {
348 // On my mac, this test fails roughly 35 times per
349 // million sends with low load, but much more with high load.
350 // Unless the workaround is in place. With 10000 sends, we
351 // should see at least a 3% failure rate.
352 const int pipes_to_send = 20000;
353 scoped_ptr<base::Thread> producer(CreateThread("producer"));
354 scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
355 scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
356 PipeChannelHelper pipe1(
357 middleman.get(),
358 consumer.get(),
359 base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
360 base::Unretained(this)),
361 pipes_to_send);
362 PipeChannelHelper pipe2(
363 producer.get(),
364 middleman.get(),
365 base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
366 pipes_to_send);
367 pipe1.Init();
368 pipe2.Init();
369 Producer(&pipe2, producer.get(), pipes_to_send);
372 private:
373 base::WaitableEvent received_;
376 TEST_F(IPCMultiSendingFdsTest, StressTest) {
377 Run();
380 } // namespace
382 #endif // defined(OS_POSIX)