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.
4
5 #include "build/build_config.h"
6
7 #if defined(OS_POSIX)
8 #if defined(OS_MACOSX)
9 extern "C" {
10 #include <sandbox.h>
11 };
12 #endif
13 #include <fcntl.h>
14 #include <stddef.h>
15 #include <sys/socket.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18
19 #include <memory>
20 #include <queue>
21
22 #include "base/callback.h"
23 #include "base/file_descriptor_posix.h"
24 #include "base/location.h"
25 #include "base/pickle.h"
26 #include "base/posix/eintr_wrapper.h"
27 #include "base/run_loop.h"
28 #include "base/single_thread_task_runner.h"
29 #include "base/synchronization/waitable_event.h"
30 #include "base/threading/thread.h"
31 #include "base/threading/thread_task_runner_handle.h"
32 #include "ipc/ipc_message_attachment_set.h"
33 #include "ipc/ipc_message_utils.h"
34 #include "ipc/ipc_test_base.h"
35
36 #if defined(OS_POSIX)
37 #include "base/macros.h"
38 #endif
39
40 #if defined(OS_MACOSX)
41 #include "sandbox/mac/seatbelt.h"
42 #endif
43
44 namespace {
45
46 const unsigned kNumFDsToSend = 7; // per message
47 const unsigned kNumMessages = 20;
48 const char* kDevZeroPath = "/dev/zero";
49
50 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
51 static_assert(kNumFDsToSend ==
52 IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage,
53 "The number of FDs to send must be kMaxDescriptorsPerMessage.");
54 #endif
55
56 class MyChannelDescriptorListenerBase : public IPC::Listener {
57 public:
OnMessageReceived(const IPC::Message & message)58 bool OnMessageReceived(const IPC::Message& message) override {
59 base::PickleIterator iter(message);
60 base::FileDescriptor descriptor;
61 while (IPC::ParamTraits<base::FileDescriptor>::Read(
62 &message, &iter, &descriptor)) {
63 HandleFD(descriptor.fd);
64 }
65 return true;
66 }
67
68 protected:
69 virtual void HandleFD(int fd) = 0;
70 };
71
72 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
73 public:
MyChannelDescriptorListener(ino_t expected_inode_num)74 explicit MyChannelDescriptorListener(ino_t expected_inode_num)
75 : MyChannelDescriptorListenerBase(),
76 expected_inode_num_(expected_inode_num),
77 num_fds_received_(0) {
78 }
79
num_fds_received() const80 unsigned num_fds_received() const {
81 return num_fds_received_;
82 }
83
OnChannelError()84 void OnChannelError() override {
85 base::RunLoop::QuitCurrentWhenIdleDeprecated();
86 }
87
88 protected:
HandleFD(int fd)89 void HandleFD(int fd) override {
90 ASSERT_GE(fd, 0);
91 // Check that we can read from the FD.
92 char buf;
93 ssize_t amt_read = read(fd, &buf, 1);
94 ASSERT_EQ(amt_read, 1);
95 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
96
97 struct stat st;
98 ASSERT_EQ(fstat(fd, &st), 0);
99
100 ASSERT_EQ(close(fd), 0);
101
102 // Compare inode numbers to check that the file sent over the wire is
103 // actually the one expected.
104 ASSERT_EQ(expected_inode_num_, st.st_ino);
105
106 ++num_fds_received_;
107 if (num_fds_received_ == kNumFDsToSend * kNumMessages)
108 base::RunLoop::QuitCurrentWhenIdleDeprecated();
109 }
110
111 private:
112 ino_t expected_inode_num_;
113 unsigned num_fds_received_;
114 };
115
116 class IPCSendFdsTest : public IPCChannelMojoTestBase {
117 protected:
RunServer()118 void RunServer() {
119 // Set up IPC channel and start client.
120 MyChannelDescriptorListener listener(-1);
121 CreateChannel(&listener);
122 ASSERT_TRUE(ConnectChannel());
123
124 for (unsigned i = 0; i < kNumMessages; ++i) {
125 IPC::Message* message =
126 new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
127 for (unsigned j = 0; j < kNumFDsToSend; ++j) {
128 const int fd = open(kDevZeroPath, O_RDONLY);
129 ASSERT_GE(fd, 0);
130 base::FileDescriptor descriptor(fd, true);
131 IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
132 }
133 ASSERT_TRUE(sender()->Send(message));
134 }
135
136 // Run message loop.
137 base::RunLoop().Run();
138
139 // Close the channel so the client's OnChannelError() gets fired.
140 channel()->Close();
141
142 EXPECT_TRUE(WaitForClientShutdown());
143 DestroyChannel();
144 }
145 };
146
TEST_F(IPCSendFdsTest,DescriptorTest)147 TEST_F(IPCSendFdsTest, DescriptorTest) {
148 Init("SendFdsClient");
149 RunServer();
150 }
151
152 class SendFdsTestClientFixture : public IpcChannelMojoTestClient {
153 protected:
SendFdsClientCommon(const std::string & test_client_name,ino_t expected_inode_num)154 void SendFdsClientCommon(const std::string& test_client_name,
155 ino_t expected_inode_num) {
156 MyChannelDescriptorListener listener(expected_inode_num);
157
158 // Set up IPC channel.
159 Connect(&listener);
160
161 // Run message loop.
162 base::RunLoop().Run();
163
164 // Verify that the message loop was exited due to getting the correct number
165 // of descriptors, and not because of the channel closing unexpectedly.
166 EXPECT_EQ(kNumFDsToSend * kNumMessages, listener.num_fds_received());
167
168 Close();
169 }
170 };
171
DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(SendFdsClient,SendFdsTestClientFixture)172 DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
173 SendFdsClient,
174 SendFdsTestClientFixture) {
175 struct stat st;
176 int fd = open(kDevZeroPath, O_RDONLY);
177 fstat(fd, &st);
178 EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
179 SendFdsClientCommon("SendFdsClient", st.st_ino);
180 }
181
182 #if defined(OS_MACOSX)
183 // Test that FDs are correctly sent to a sandboxed process.
184 // TODO(port): Make this test cross-platform.
TEST_F(IPCSendFdsTest,DescriptorTestSandboxed)185 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
186 Init("SendFdsSandboxedClient");
187 RunServer();
188 }
189
DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(SendFdsSandboxedClient,SendFdsTestClientFixture)190 DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
191 SendFdsSandboxedClient,
192 SendFdsTestClientFixture) {
193 struct stat st;
194 const int fd = open(kDevZeroPath, O_RDONLY);
195 fstat(fd, &st);
196 ASSERT_LE(0, IGNORE_EINTR(close(fd)));
197
198 // Enable the sandbox.
199 char* error_buff = NULL;
200 int error = sandbox::Seatbelt::Init(
201 sandbox::Seatbelt::kProfilePureComputation, SANDBOX_NAMED, &error_buff);
202 ASSERT_EQ(0, error);
203 ASSERT_FALSE(error_buff);
204
205 sandbox::Seatbelt::FreeError(error_buff);
206
207 // Make sure sandbox is really enabled.
208 ASSERT_EQ(-1, open(kDevZeroPath, O_RDONLY))
209 << "Sandbox wasn't properly enabled";
210
211 // See if we can receive a file descriptor.
212 SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
213 }
214 #endif // defined(OS_MACOSX)
215
216 } // namespace
217
218 #endif // defined(OS_POSIX)
219