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