1 // Copyright 2014 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 "mojo/edk/embedder/platform_channel_pair.h"
6
7 #include <errno.h>
8 #include <poll.h>
9 #include <signal.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <sys/socket.h>
13 #include <sys/types.h>
14 #include <sys/uio.h>
15 #include <unistd.h>
16 #include <deque>
17 #include <utility>
18
19 #include "base/files/file_path.h"
20 #include "base/files/file_util.h"
21 #include "base/files/scoped_file.h"
22 #include "base/files/scoped_temp_dir.h"
23 #include "base/logging.h"
24 #include "base/macros.h"
25 #include "mojo/edk/embedder/platform_channel_utils_posix.h"
26 #include "mojo/edk/embedder/platform_handle.h"
27 #include "mojo/edk/embedder/platform_handle_vector.h"
28 #include "mojo/edk/embedder/scoped_platform_handle.h"
29 #include "mojo/edk/test/test_utils.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace mojo {
33 namespace edk {
34 namespace {
35
WaitReadable(PlatformHandle h)36 void WaitReadable(PlatformHandle h) {
37 struct pollfd pfds = {};
38 pfds.fd = h.handle;
39 pfds.events = POLLIN;
40 CHECK_EQ(poll(&pfds, 1, -1), 1);
41 }
42
43 class PlatformChannelPairPosixTest : public testing::Test {
44 public:
PlatformChannelPairPosixTest()45 PlatformChannelPairPosixTest() {}
~PlatformChannelPairPosixTest()46 ~PlatformChannelPairPosixTest() override {}
47
SetUp()48 void SetUp() override {
49 // Make sure |SIGPIPE| isn't being ignored.
50 struct sigaction action = {};
51 action.sa_handler = SIG_DFL;
52 ASSERT_EQ(0, sigaction(SIGPIPE, &action, &old_action_));
53 }
54
TearDown()55 void TearDown() override {
56 // Restore the |SIGPIPE| handler.
57 ASSERT_EQ(0, sigaction(SIGPIPE, &old_action_, nullptr));
58 }
59
60 private:
61 struct sigaction old_action_;
62
63 DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairPosixTest);
64 };
65
TEST_F(PlatformChannelPairPosixTest,NoSigPipe)66 TEST_F(PlatformChannelPairPosixTest, NoSigPipe) {
67 PlatformChannelPair channel_pair;
68 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
69 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
70
71 // Write to the client.
72 static const char kHello[] = "hello";
73 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
74 write(client_handle.get().handle, kHello, sizeof(kHello)));
75
76 // Close the client.
77 client_handle.reset();
78
79 // Read from the server; this should be okay.
80 char buffer[100] = {};
81 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
82 read(server_handle.get().handle, buffer, sizeof(buffer)));
83 EXPECT_STREQ(kHello, buffer);
84
85 // Try reading again.
86 ssize_t result = read(server_handle.get().handle, buffer, sizeof(buffer));
87 // We should probably get zero (for "end of file"), but -1 would also be okay.
88 EXPECT_TRUE(result == 0 || result == -1);
89 if (result == -1)
90 PLOG(WARNING) << "read (expected 0 for EOF)";
91
92 // Test our replacement for |write()|/|send()|.
93 result = PlatformChannelWrite(server_handle.get(), kHello, sizeof(kHello));
94 EXPECT_EQ(-1, result);
95 if (errno != EPIPE)
96 PLOG(WARNING) << "write (expected EPIPE)";
97
98 // Test our replacement for |writev()|/|sendv()|.
99 struct iovec iov[2] = {{const_cast<char*>(kHello), sizeof(kHello)},
100 {const_cast<char*>(kHello), sizeof(kHello)}};
101 result = PlatformChannelWritev(server_handle.get(), iov, 2);
102 EXPECT_EQ(-1, result);
103 if (errno != EPIPE)
104 PLOG(WARNING) << "write (expected EPIPE)";
105 }
106
TEST_F(PlatformChannelPairPosixTest,SendReceiveData)107 TEST_F(PlatformChannelPairPosixTest, SendReceiveData) {
108 PlatformChannelPair channel_pair;
109 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
110 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
111
112 for (size_t i = 0; i < 10; i++) {
113 std::string send_string(1 << i, 'A' + i);
114
115 EXPECT_EQ(static_cast<ssize_t>(send_string.size()),
116 PlatformChannelWrite(server_handle.get(), send_string.data(),
117 send_string.size()));
118
119 WaitReadable(client_handle.get());
120
121 char buf[10000] = {};
122 std::deque<PlatformHandle> received_handles;
123 ssize_t result = PlatformChannelRecvmsg(client_handle.get(), buf,
124 sizeof(buf), &received_handles);
125 EXPECT_EQ(static_cast<ssize_t>(send_string.size()), result);
126 EXPECT_EQ(send_string, std::string(buf, static_cast<size_t>(result)));
127 EXPECT_TRUE(received_handles.empty());
128 }
129 }
130
TEST_F(PlatformChannelPairPosixTest,SendReceiveFDs)131 TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) {
132 base::ScopedTempDir temp_dir;
133 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
134
135 static const char kHello[] = "hello";
136
137 PlatformChannelPair channel_pair;
138 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
139 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
140
141 // Reduce the number of FDs opened on OS X to avoid test flake.
142 #if defined(OS_MACOSX)
143 const size_t kNumHandlesToSend = kPlatformChannelMaxNumHandles / 2;
144 #else
145 const size_t kNumHandlesToSend = kPlatformChannelMaxNumHandles;
146 #endif
147
148 for (size_t i = 1; i < kNumHandlesToSend; i++) {
149 // Make |i| files, with the j-th file consisting of j copies of the digit
150 // |c|.
151 const char c = '0' + (i % 10);
152 ScopedPlatformHandleVectorPtr platform_handles(new PlatformHandleVector);
153 for (size_t j = 1; j <= i; j++) {
154 base::FilePath unused;
155 base::ScopedFILE fp(
156 base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
157 ASSERT_TRUE(fp);
158 ASSERT_EQ(j, fwrite(std::string(j, c).data(), 1, j, fp.get()));
159 platform_handles->push_back(
160 test::PlatformHandleFromFILE(std::move(fp)).release());
161 ASSERT_TRUE(platform_handles->back().is_valid());
162 }
163
164 // Send the FDs (+ "hello").
165 struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)};
166 // We assume that the |sendmsg()| actually sends all the data.
167 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
168 PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1,
169 &platform_handles->at(0),
170 platform_handles->size()));
171
172 WaitReadable(client_handle.get());
173
174 char buf[10000] = {};
175 std::deque<PlatformHandle> received_handles;
176 // We assume that the |recvmsg()| actually reads all the data.
177 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
178 PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf),
179 &received_handles));
180 EXPECT_STREQ(kHello, buf);
181 EXPECT_EQ(i, received_handles.size());
182
183 for (size_t j = 0; !received_handles.empty(); j++) {
184 base::ScopedFILE fp(test::FILEFromPlatformHandle(
185 ScopedPlatformHandle(received_handles.front()), "rb"));
186 received_handles.pop_front();
187 ASSERT_TRUE(fp);
188 rewind(fp.get());
189 char read_buf[kNumHandlesToSend];
190 size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
191 EXPECT_EQ(j + 1, bytes_read);
192 EXPECT_EQ(std::string(j + 1, c), std::string(read_buf, bytes_read));
193 }
194 }
195 }
196
TEST_F(PlatformChannelPairPosixTest,AppendReceivedFDs)197 TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) {
198 base::ScopedTempDir temp_dir;
199 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
200
201 static const char kHello[] = "hello";
202
203 PlatformChannelPair channel_pair;
204 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle();
205 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle();
206
207 const std::string file_contents("hello world");
208
209 {
210 base::FilePath unused;
211 base::ScopedFILE fp(
212 base::CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
213 ASSERT_TRUE(fp);
214 ASSERT_EQ(file_contents.size(),
215 fwrite(file_contents.data(), 1, file_contents.size(), fp.get()));
216 ScopedPlatformHandleVectorPtr platform_handles(new PlatformHandleVector);
217 platform_handles->push_back(
218 test::PlatformHandleFromFILE(std::move(fp)).release());
219 ASSERT_TRUE(platform_handles->back().is_valid());
220
221 // Send the FD (+ "hello").
222 struct iovec iov = {const_cast<char*>(kHello), sizeof(kHello)};
223 // We assume that the |sendmsg()| actually sends all the data.
224 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
225 PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1,
226 &platform_handles->at(0),
227 platform_handles->size()));
228 }
229
230 WaitReadable(client_handle.get());
231
232 // Start with an invalid handle in the deque.
233 std::deque<PlatformHandle> received_handles;
234 received_handles.push_back(PlatformHandle());
235
236 char buf[100] = {};
237 // We assume that the |recvmsg()| actually reads all the data.
238 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
239 PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf),
240 &received_handles));
241 EXPECT_STREQ(kHello, buf);
242 ASSERT_EQ(2u, received_handles.size());
243 EXPECT_FALSE(received_handles[0].is_valid());
244 EXPECT_TRUE(received_handles[1].is_valid());
245
246 {
247 base::ScopedFILE fp(test::FILEFromPlatformHandle(
248 ScopedPlatformHandle(received_handles[1]), "rb"));
249 received_handles[1] = PlatformHandle();
250 ASSERT_TRUE(fp);
251 rewind(fp.get());
252 char read_buf[100];
253 size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
254 EXPECT_EQ(file_contents.size(), bytes_read);
255 EXPECT_EQ(file_contents, std::string(read_buf, bytes_read));
256 }
257 }
258
259 } // namespace
260 } // namespace edk
261 } // namespace mojo
262