• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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