Home | History | Annotate | Download | only in embedder
      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 
     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:
     45   PlatformChannelPairPosixTest() {}
     46   ~PlatformChannelPairPosixTest() override {}
     47 
     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 
     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 
     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 
    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 
    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.GetPath(), &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 
    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.GetPath(), &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