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