xref: /aosp_15_r20/external/cronet/base/mac/mach_port_rendezvous_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 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 "base/mac/mach_port_rendezvous.h"
6 
7 #include <mach/mach.h>
8 
9 #include <utility>
10 
11 #include "base/apple/foundation_util.h"
12 #include "base/apple/mach_logging.h"
13 #include "base/at_exit.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/test/multiprocess_test.h"
16 #include "base/test/test_timeouts.h"
17 #include "base/threading/platform_thread.h"
18 #include "base/time/time.h"
19 #include "testing/multiprocess_func_list.h"
20 
21 namespace base {
22 
23 namespace {
24 
25 constexpr MachPortsForRendezvous::key_type kTestPortKey = 'port';
26 
27 }  // namespace
28 
29 class MachPortRendezvousServerTest : public MultiProcessTest {
30  public:
SetUp()31   void SetUp() override {}
32 
client_data()33   std::map<pid_t, MachPortRendezvousServer::ClientData>& client_data() {
34     return MachPortRendezvousServer::GetInstance()->client_data_;
35   }
36 
37  private:
38   ShadowingAtExitManager at_exit_;
39 };
40 
MULTIPROCESS_TEST_MAIN(TakeSendRight)41 MULTIPROCESS_TEST_MAIN(TakeSendRight) {
42   auto* rendezvous_client = MachPortRendezvousClient::GetInstance();
43   CHECK(rendezvous_client);
44 
45   CHECK_EQ(1u, rendezvous_client->GetPortCount());
46 
47   apple::ScopedMachSendRight port =
48       rendezvous_client->TakeSendRight(kTestPortKey);
49   CHECK(port.is_valid());
50 
51   mach_msg_base_t msg{};
52   msg.header.msgh_bits = MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_COPY_SEND);
53   msg.header.msgh_size = sizeof(msg);
54   msg.header.msgh_remote_port = port.get();
55   msg.header.msgh_id = 'good';
56 
57   kern_return_t kr =
58       mach_msg(&msg.header, MACH_SEND_MSG, msg.header.msgh_size, 0,
59                MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
60   MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg";
61 
62   return 0;
63 }
64 
TEST_F(MachPortRendezvousServerTest,SendRight)65 TEST_F(MachPortRendezvousServerTest, SendRight) {
66   auto* server = MachPortRendezvousServer::GetInstance();
67   ASSERT_TRUE(server);
68 
69   apple::ScopedMachReceiveRight port;
70   kern_return_t kr =
71       mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
72                          apple::ScopedMachReceiveRight::Receiver(port).get());
73   ASSERT_EQ(kr, KERN_SUCCESS);
74 
75   MachRendezvousPort rendezvous_port(port.get(), MACH_MSG_TYPE_MAKE_SEND);
76 
77   Process child;
78   {
79     AutoLock lock(server->GetLock());
80     child = SpawnChild("TakeSendRight");
81     server->RegisterPortsForPid(
82         child.Pid(), {std::make_pair(kTestPortKey, rendezvous_port)});
83   }
84 
85   struct : mach_msg_base_t {
86     mach_msg_trailer_t trailer;
87   } msg{};
88   kr = mach_msg(&msg.header, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg),
89                 port.get(), TestTimeouts::action_timeout().InMilliseconds(),
90                 MACH_PORT_NULL);
91 
92   EXPECT_EQ(kr, KERN_SUCCESS) << mach_error_string(kr);
93   EXPECT_EQ(msg.header.msgh_id, 'good');
94 
95   int exit_code;
96   ASSERT_TRUE(WaitForMultiprocessTestChildExit(
97       child, TestTimeouts::action_timeout(), &exit_code));
98 
99   EXPECT_EQ(0, exit_code);
100 }
101 
MULTIPROCESS_TEST_MAIN(NoRights)102 MULTIPROCESS_TEST_MAIN(NoRights) {
103   auto* rendezvous_client = MachPortRendezvousClient::GetInstance();
104   CHECK(rendezvous_client);
105   CHECK_EQ(0u, rendezvous_client->GetPortCount());
106   return 0;
107 }
108 
TEST_F(MachPortRendezvousServerTest,NoRights)109 TEST_F(MachPortRendezvousServerTest, NoRights) {
110   auto* server = MachPortRendezvousServer::GetInstance();
111   ASSERT_TRUE(server);
112 
113   Process child = SpawnChild("NoRights");
114 
115   int exit_code;
116   ASSERT_TRUE(WaitForMultiprocessTestChildExit(
117       child, TestTimeouts::action_timeout(), &exit_code));
118 
119   EXPECT_EQ(0, exit_code);
120 }
121 
MULTIPROCESS_TEST_MAIN(Exit42)122 MULTIPROCESS_TEST_MAIN(Exit42) {
123   _exit(42);
124 }
125 
TEST_F(MachPortRendezvousServerTest,CleanupIfNoRendezvous)126 TEST_F(MachPortRendezvousServerTest, CleanupIfNoRendezvous) {
127   auto* server = MachPortRendezvousServer::GetInstance();
128   ASSERT_TRUE(server);
129 
130   apple::ScopedMachReceiveRight port;
131   kern_return_t kr =
132       mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
133                          apple::ScopedMachReceiveRight::Receiver(port).get());
134   ASSERT_EQ(kr, KERN_SUCCESS);
135 
136   MachRendezvousPort rendezvous_port(port.get(), MACH_MSG_TYPE_MAKE_SEND);
137 
138   Process child;
139   {
140     AutoLock lock(server->GetLock());
141     child = SpawnChild("Exit42");
142     server->RegisterPortsForPid(
143         child.Pid(), {std::make_pair(kTestPortKey, rendezvous_port)});
144 
145     EXPECT_EQ(1u, client_data().size());
146   }
147 
148   int exit_code;
149   ASSERT_TRUE(WaitForMultiprocessTestChildExit(
150       child, TestTimeouts::action_timeout(), &exit_code));
151 
152   EXPECT_EQ(42, exit_code);
153 
154   // There is no way to synchronize the test code with the asynchronous
155   // delivery of the dispatch process-exit notification. Loop for a short
156   // while for it to be delivered.
157   auto start = TimeTicks::Now();
158   do {
159     if (client_data().size() == 0)
160       break;
161     // Sleep is fine because dispatch will process the notification on one of
162     // its workers.
163     PlatformThread::Sleep(Milliseconds(10));
164   } while ((TimeTicks::Now() - start) < TestTimeouts::action_timeout());
165 
166   EXPECT_EQ(0u, client_data().size());
167 }
168 
TEST_F(MachPortRendezvousServerTest,DestroyRight)169 TEST_F(MachPortRendezvousServerTest, DestroyRight) {
170   const struct {
171     // How to create the port.
172     bool insert_send_right;
173 
174     // Disposition for MachRendezvousPort.
175     mach_port_right_t disposition;
176 
177     // After calling DestroyRight.
178     bool is_dead_name;
179     mach_port_urefs_t send_rights;
180   } kCases[] = {
181       {true, MACH_MSG_TYPE_MOVE_RECEIVE, true, 0},
182       {true, MACH_MSG_TYPE_MOVE_SEND, false, 0},
183       {true, MACH_MSG_TYPE_COPY_SEND, false, 1},
184       {true, MACH_MSG_TYPE_MAKE_SEND, false, 1},
185       {false, MACH_MSG_TYPE_MAKE_SEND, false, 0},
186       {true, MACH_MSG_TYPE_MAKE_SEND_ONCE, false, 1},
187       // It's not possible to test MOVE_SEND_ONCE since one cannot
188       // insert_right MAKE_SEND_ONCE.
189   };
190 
191   for (size_t i = 0; i < std::size(kCases); ++i) {
192     SCOPED_TRACE(base::StringPrintf("case %zu", i).c_str());
193     const auto& test = kCases[i];
194 
195     // This test deliberately leaks Mach port rights.
196     mach_port_t port;
197     kern_return_t kr =
198         mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
199     ASSERT_EQ(kr, KERN_SUCCESS);
200 
201     if (test.insert_send_right) {
202       kr = mach_port_insert_right(mach_task_self(), port, port,
203                                   MACH_MSG_TYPE_MAKE_SEND);
204       ASSERT_EQ(kr, KERN_SUCCESS);
205     }
206 
207     MachRendezvousPort rendezvous_port(port, test.disposition);
208     rendezvous_port.Destroy();
209 
210     mach_port_type_t type = 0;
211     kr = mach_port_type(mach_task_self(), port, &type);
212     ASSERT_EQ(kr, KERN_SUCCESS);
213 
214     EXPECT_EQ(type == MACH_PORT_TYPE_DEAD_NAME, test.is_dead_name) << type;
215 
216     mach_port_urefs_t refs = 0;
217     kr =
218         mach_port_get_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, &refs);
219     ASSERT_EQ(kr, KERN_SUCCESS);
220     EXPECT_EQ(refs, test.send_rights);
221   }
222 }
223 
MULTIPROCESS_TEST_MAIN(FailToRendezvous)224 MULTIPROCESS_TEST_MAIN(FailToRendezvous) {
225   // The rendezvous system uses the BaseBundleID to construct the bootstrap
226   // server name, so changing it will result in a failure to look it up.
227   base::apple::SetBaseBundleID("org.chromium.totallyfake");
228   CHECK_EQ(nullptr, base::MachPortRendezvousClient::GetInstance());
229   return 0;
230 }
231 
TEST_F(MachPortRendezvousServerTest,FailToRendezvous)232 TEST_F(MachPortRendezvousServerTest, FailToRendezvous) {
233   auto* server = MachPortRendezvousServer::GetInstance();
234   ASSERT_TRUE(server);
235 
236   Process child = SpawnChild("FailToRendezvous");
237 
238   int exit_code;
239   ASSERT_TRUE(WaitForMultiprocessTestChildExit(
240       child, TestTimeouts::action_timeout(), &exit_code));
241 
242   EXPECT_EQ(0, exit_code);
243 }
244 
245 }  // namespace base
246