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