1 // Copyright 2012 The Chromium Authors
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 BUILDFLAG(IS_MAC)
8 extern "C" {
9 #include <sandbox.h>
10 }
11 #endif
12
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/file_descriptor_posix.h"
23 #include "base/pickle.h"
24 #include "base/posix/eintr_wrapper.h"
25 #include "base/run_loop.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "base/task/single_thread_task_runner.h"
28 #include "base/threading/thread.h"
29 #include "ipc/ipc_message_attachment_set.h"
30 #include "ipc/ipc_message_utils.h"
31 #include "ipc/ipc_test_base.h"
32
33 #if BUILDFLAG(IS_MAC)
34 #include "sandbox/mac/seatbelt.h"
35 #elif BUILDFLAG(IS_FUCHSIA)
36 #include "base/memory/scoped_refptr.h"
37 #include "base/test/scoped_dev_zero_fuchsia.h"
38 #endif
39
40 namespace {
41
42 const unsigned kNumFDsToSend = 7; // per message
43 const unsigned kNumMessages = 20;
44 const char* kDevZeroPath = "/dev/zero";
45
46 static_assert(kNumFDsToSend ==
47 IPC::MessageAttachmentSet::kMaxDescriptorsPerMessage,
48 "The number of FDs to send must be kMaxDescriptorsPerMessage.");
49
50 class MyChannelDescriptorListenerBase : public IPC::Listener {
51 public:
OnMessageReceived(const IPC::Message & message)52 bool OnMessageReceived(const IPC::Message& message) override {
53 base::PickleIterator iter(message);
54 base::FileDescriptor descriptor;
55 while (IPC::ParamTraits<base::FileDescriptor>::Read(
56 &message, &iter, &descriptor)) {
57 HandleFD(descriptor.fd);
58 }
59 return true;
60 }
61
62 protected:
63 virtual void HandleFD(int fd) = 0;
64 };
65
66 class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
67 public:
MyChannelDescriptorListener(ino_t expected_inode_num)68 explicit MyChannelDescriptorListener(ino_t expected_inode_num)
69 : MyChannelDescriptorListenerBase(),
70 expected_inode_num_(expected_inode_num),
71 num_fds_received_(0) {
72 }
73
num_fds_received() const74 unsigned num_fds_received() const {
75 return num_fds_received_;
76 }
Run()77 void Run() { loop_.Run(); }
OnChannelError()78 void OnChannelError() override { loop_.QuitWhenIdle(); }
79
80 protected:
HandleFD(int fd)81 void HandleFD(int fd) override {
82 ASSERT_GE(fd, 0);
83 // Check that we can read from the FD.
84 char buf;
85 ssize_t amt_read = read(fd, &buf, 1);
86 ASSERT_EQ(amt_read, 1);
87 ASSERT_EQ(buf, 0); // /dev/zero always reads 0 bytes.
88
89 struct stat st;
90 ASSERT_EQ(fstat(fd, &st), 0);
91
92 ASSERT_EQ(close(fd), 0);
93
94 // Compare inode numbers to check that the file sent over the wire is
95 // actually the one expected.
96 ASSERT_EQ(expected_inode_num_, st.st_ino);
97
98 ++num_fds_received_;
99 if (num_fds_received_ == kNumFDsToSend * kNumMessages) {
100 loop_.QuitWhenIdle();
101 }
102 }
103
104 private:
105 ino_t expected_inode_num_;
106 unsigned num_fds_received_;
107 base::RunLoop loop_;
108 };
109
110 class IPCSendFdsTest : public IPCChannelMojoTestBase {
111 protected:
SetUp()112 void SetUp() override {
113 #if BUILDFLAG(IS_FUCHSIA)
114 ASSERT_TRUE(dev_zero_);
115 #endif
116 }
117
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 listener.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 private:
147 #if BUILDFLAG(IS_FUCHSIA)
148 scoped_refptr<base::ScopedDevZero> dev_zero_ = base::ScopedDevZero::Get();
149 #endif
150 };
151
152 // Disabled on Fuchsia due to failures; see https://crbug.com/1272424.
153 #if BUILDFLAG(IS_FUCHSIA)
154 #define MAYBE_DescriptorTest DISABLED_DescriptorTest
155 #else
156 #define MAYBE_DescriptorTest DescriptorTest
157 #endif
TEST_F(IPCSendFdsTest,MAYBE_DescriptorTest)158 TEST_F(IPCSendFdsTest, MAYBE_DescriptorTest) {
159 Init("SendFdsClient");
160 RunServer();
161 }
162
163 class SendFdsTestClientFixture : public IpcChannelMojoTestClient {
164 protected:
SendFdsClientCommon(const std::string & test_client_name,ino_t expected_inode_num)165 void SendFdsClientCommon(const std::string& test_client_name,
166 ino_t expected_inode_num) {
167 MyChannelDescriptorListener listener(expected_inode_num);
168
169 // Set up IPC channel.
170 Connect(&listener);
171
172 // Run message loop.
173 listener.Run();
174
175 // Verify that the message loop was exited due to getting the correct number
176 // of descriptors, and not because of the channel closing unexpectedly.
177 EXPECT_EQ(kNumFDsToSend * kNumMessages, listener.num_fds_received());
178
179 Close();
180 }
181 };
182
DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(SendFdsClient,SendFdsTestClientFixture)183 DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
184 SendFdsClient,
185 SendFdsTestClientFixture) {
186 struct stat st;
187 int fd = open(kDevZeroPath, O_RDONLY);
188 fstat(fd, &st);
189 EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
190 SendFdsClientCommon("SendFdsClient", st.st_ino);
191 }
192
193 #if BUILDFLAG(IS_MAC)
194 // Test that FDs are correctly sent to a sandboxed process.
195 // TODO(port): Make this test cross-platform.
TEST_F(IPCSendFdsTest,DescriptorTestSandboxed)196 TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
197 Init("SendFdsSandboxedClient");
198 RunServer();
199 }
200
DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(SendFdsSandboxedClient,SendFdsTestClientFixture)201 DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT_WITH_CUSTOM_FIXTURE(
202 SendFdsSandboxedClient,
203 SendFdsTestClientFixture) {
204 struct stat st;
205 const int fd = open(kDevZeroPath, O_RDONLY);
206 fstat(fd, &st);
207 ASSERT_LE(0, IGNORE_EINTR(close(fd)));
208
209 // Enable the sandbox.
210 std::string error;
211 ASSERT_TRUE(sandbox::Seatbelt::Init(
212 sandbox::Seatbelt::kProfilePureComputation, SANDBOX_NAMED, &error))
213 << error;
214
215 // Make sure sandbox is really enabled.
216 ASSERT_EQ(-1, open(kDevZeroPath, O_RDONLY))
217 << "Sandbox wasn't properly enabled";
218
219 // See if we can receive a file descriptor.
220 SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
221 }
222 #endif // BUILDFLAG(IS_MAC)
223
224 } // namespace
225