blob: 005103c3dbb8cd3c38d35be6bf7b0507bb8e6200 [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
agl@chromium.org1c6dcf22009-07-23 08:57:21 +09007#if defined(OS_MACOSX)
8extern "C" {
9#include <sandbox.h>
10}
11#endif
12#include <fcntl.h>
13#include <sys/stat.h>
14
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090015#include "base/message_loop.h"
brettw@chromium.orgb1788fb2012-11-15 05:54:35 +090016#include "base/posix/eintr_wrapper.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090017#include "ipc/ipc_channel.h"
tfarina@chromium.orgbeba2922012-11-18 04:20:05 +090018#include "ipc/ipc_listener.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090019#include "ipc/ipc_message_utils.h"
jcivelli@chromium.orgc30c76f2012-06-27 10:12:24 +090020#include "ipc/ipc_multiprocess_test.h"
viettrungluu@chromium.org7d86af22013-01-12 00:13:37 +090021#include "ipc/ipc_test_base.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090022
23#if defined(OS_POSIX)
erg@google.come6ffcb52010-08-18 03:38:24 +090024#include "base/file_descriptor_posix.h"
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090025
26namespace {
27
28const unsigned kNumFDsToSend = 20;
29const char* kDevZeroPath = "/dev/zero";
30
31static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) {
32 // Check that we can read from the FD.
33 char buf;
34 ssize_t amt_read = read(fd, &buf, 1);
35 ASSERT_EQ(amt_read, 1);
36 ASSERT_EQ(buf, 0); // /dev/zero always reads NUL bytes.
37
38 struct stat st;
39 ASSERT_EQ(fstat(fd, &st), 0);
40
41 ASSERT_EQ(close(fd), 0);
42
43 // We compare iNode numbers to check that the file sent over the wire
44 // was actually the same physical file as the one we were expecting.
45 ASSERT_EQ(inode_num, st.st_ino);
46}
47
brettw@chromium.orgdb1259e2012-06-30 07:05:26 +090048class MyChannelDescriptorListener : public IPC::Listener {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090049 public:
50 MyChannelDescriptorListener(ino_t expected_inode_num)
51 : expected_inode_num_(expected_inode_num),
52 num_fds_received_(0) {}
53
jam@chromium.org8a2c7842010-12-24 15:19:28 +090054 virtual bool OnMessageReceived(const IPC::Message& message) {
jbates@chromium.org0fc87362012-03-08 05:42:56 +090055 PickleIterator iter(message);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090056
57 ++num_fds_received_;
58 base::FileDescriptor descriptor;
59
jam@chromium.org8a2c7842010-12-24 15:19:28 +090060 IPC::ParamTraits<base::FileDescriptor>::Read(
61 &message, &iter, &descriptor);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090062
63 VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_);
64 if (num_fds_received_ == kNumFDsToSend) {
65 MessageLoop::current()->Quit();
66 }
jam@chromium.org8a2c7842010-12-24 15:19:28 +090067 return true;
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090068 }
69
70 virtual void OnChannelError() {
71 MessageLoop::current()->Quit();
72 }
dmaclach@chromium.org63b5df72010-12-09 10:12:20 +090073
74 bool GotExpectedNumberOfDescriptors() {
75 return kNumFDsToSend == num_fds_received_;
76 }
77
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090078 private:
79 ino_t expected_inode_num_;
80 unsigned num_fds_received_;
81};
82
tfarina@chromium.orgbeba2922012-11-18 04:20:05 +090083void TestDescriptorServer(IPC::Channel& chan,
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090084 base::ProcessHandle process_handle) {
85 ASSERT_TRUE(process_handle);
86
87 for (unsigned i = 0; i < kNumFDsToSend; ++i) {
88 base::FileDescriptor descriptor;
89 const int fd = open(kDevZeroPath, O_RDONLY);
90 ASSERT_GE(fd, 0);
91 descriptor.auto_close = true;
92 descriptor.fd = fd;
93
94 IPC::Message* message = new IPC::Message(0, // routing_id
95 3, // message type
96 IPC::Message::PRIORITY_NORMAL);
97 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
dmaclach@chromium.org63b5df72010-12-09 10:12:20 +090098 ASSERT_TRUE(chan.Send(message));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +090099 }
100
101 // Run message loop.
102 MessageLoop::current()->Run();
103
104 // Close Channel so client gets its OnChannelError() callback fired.
105 chan.Close();
106
107 // Cleanup child process.
tedvessenes@gmail.com5966b642012-07-12 00:41:43 +0900108 EXPECT_TRUE(base::WaitForSingleProcess(
109 process_handle, base::TimeDelta::FromSeconds(5)));
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900110}
111
112int TestDescriptorClient(ino_t expected_inode_num) {
113 MessageLoopForIO main_message_loop;
114 MyChannelDescriptorListener listener(expected_inode_num);
115
116 // Setup IPC channel.
117 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT,
118 &listener);
evan@chromium.org1f3e46b2010-10-20 04:11:15 +0900119 CHECK(chan.Connect());
dmaclach@chromium.org63b5df72010-12-09 10:12:20 +0900120
121 // Run message loop so IPC Channel can handle message IO.
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900122 MessageLoop::current()->Run();
123
dmaclach@chromium.org63b5df72010-12-09 10:12:20 +0900124 // Verify that the message loop was exited due to getting the correct
125 // number of descriptors, and not because the channel closing unexpectedly.
126 CHECK(listener.GotExpectedNumberOfDescriptors());
127
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900128 return 0;
129}
130
viettrungluu@chromium.org7d86af22013-01-12 00:13:37 +0900131class IPCSendFdsTest : public IPCTestBase {
132};
133
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900134#if defined(OS_MACOSX)
135// TODO(port): Make this test cross-platform.
jcivelli@chromium.orgc30c76f2012-06-27 10:12:24 +0900136MULTIPROCESS_IPC_TEST_MAIN(RunTestDescriptorClientSandboxed) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900137 struct stat st;
138 const int fd = open(kDevZeroPath, O_RDONLY);
139 fstat(fd, &st);
thakis@chromium.org5dab7482010-10-04 09:34:04 +0900140 if (HANDLE_EINTR(close(fd)) < 0) {
141 return -1;
142 }
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900143
144 // Enable the Sandbox.
145 char* error_buff = NULL;
146 int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
147 &error_buff);
148 bool success = (error == 0 && error_buff == NULL);
149 if (!success) {
150 return -1;
151 }
152
153 sandbox_free_error(error_buff);
154
155 // Make sure Sandbox is really enabled.
156 if (open(kDevZeroPath, O_RDONLY) != -1) {
157 LOG(ERROR) << "Sandbox wasn't properly enabled";
158 return -1;
159 }
160
161 // See if we can receive a file descriptor.
162 return TestDescriptorClient(st.st_ino);
163}
164
165// Test that FDs are correctly sent to a sandboxed process.
viettrungluu@chromium.org7d86af22013-01-12 00:13:37 +0900166TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900167 // Setup IPC channel.
168 MyChannelDescriptorListener listener(-1);
169
170 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
171 &listener);
evan@chromium.org1f3e46b2010-10-20 04:11:15 +0900172 ASSERT_TRUE(chan.Connect());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900173
174 base::ProcessHandle process_handle = SpawnChild(
175 TEST_DESCRIPTOR_CLIENT_SANDBOXED,
176 &chan);
177 TestDescriptorServer(chan, process_handle);
178}
179#endif // defined(OS_MACOSX)
180
jcivelli@chromium.orgc30c76f2012-06-27 10:12:24 +0900181MULTIPROCESS_IPC_TEST_MAIN(RunTestDescriptorClient) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900182 struct stat st;
183 const int fd = open(kDevZeroPath, O_RDONLY);
184 fstat(fd, &st);
hans@chromium.org544d9e62010-09-16 17:15:32 +0900185 EXPECT_GE(HANDLE_EINTR(close(fd)), 0);
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900186
187 return TestDescriptorClient(st.st_ino);
188}
189
viettrungluu@chromium.org7d86af22013-01-12 00:13:37 +0900190TEST_F(IPCSendFdsTest, DescriptorTest) {
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900191 // Setup IPC channel.
192 MyChannelDescriptorListener listener(-1);
193
194 IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER,
195 &listener);
evan@chromium.org1f3e46b2010-10-20 04:11:15 +0900196 ASSERT_TRUE(chan.Connect());
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900197
198 base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT,
199 &chan);
200 TestDescriptorServer(chan, process_handle);
201}
202
viettrungluu@chromium.org7ca19132013-01-12 05:56:22 +0900203} // namespace
204
agl@chromium.org1c6dcf22009-07-23 08:57:21 +0900205#endif // defined(OS_POSIX)