1 // Copyright 2018 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 <cstdint>
6 #include <string>
7
8 #include "base/base_paths.h"
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/optional.h"
16 #include "base/path_service.h"
17 #include "base/run_loop.h"
18 #include "base/synchronization/lock.h"
19 #include "base/test/multiprocess_test.h"
20 #include "base/test/scoped_task_environment.h"
21 #include "base/threading/sequenced_task_runner_handle.h"
22 #include "build/build_config.h"
23 #include "mojo/core/test/mojo_test_base.h"
24 #include "mojo/public/c/system/invitation.h"
25 #include "mojo/public/cpp/platform/named_platform_channel.h"
26 #include "mojo/public/cpp/platform/platform_channel.h"
27 #include "mojo/public/cpp/system/platform_handle.h"
28
29 namespace mojo {
30 namespace core {
31 namespace {
32
33 enum class TransportType {
34 kChannel,
35 kChannelServer,
36 };
37
38 const char kSecondaryChannelHandleSwitch[] = "test-secondary-channel-handle";
39
40 class InvitationTest : public test::MojoTestBase {
41 public:
42 InvitationTest() = default;
43 ~InvitationTest() override = default;
44
45 protected:
46 static base::Process LaunchChildTestClient(
47 const std::string& test_client_name,
48 MojoHandle* primordial_pipes,
49 size_t num_primordial_pipes,
50 TransportType transport_type,
51 MojoSendInvitationFlags send_flags,
52 MojoProcessErrorHandler error_handler = nullptr,
53 uintptr_t error_handler_context = 0,
54 base::CommandLine* custom_command_line = nullptr,
55 base::LaunchOptions* custom_launch_options = nullptr);
56
57 static void SendInvitationToClient(
58 PlatformHandle endpoint_handle,
59 base::ProcessHandle process,
60 MojoHandle* primordial_pipes,
61 size_t num_primordial_pipes,
62 TransportType transport_type,
63 MojoSendInvitationFlags flags,
64 MojoProcessErrorHandler error_handler,
65 uintptr_t error_handler_context,
66 base::StringPiece isolated_invitation_name);
67
68 private:
69 base::test::ScopedTaskEnvironment task_environment_;
70
71 DISALLOW_COPY_AND_ASSIGN(InvitationTest);
72 };
73
PrepareToPassRemoteEndpoint(PlatformChannel * channel,base::LaunchOptions * options,base::CommandLine * command_line,base::StringPiece switch_name={})74 void PrepareToPassRemoteEndpoint(PlatformChannel* channel,
75 base::LaunchOptions* options,
76 base::CommandLine* command_line,
77 base::StringPiece switch_name = {}) {
78 std::string value;
79 #if defined(OS_FUCHSIA)
80 channel->PrepareToPassRemoteEndpoint(&options->handles_to_transfer, &value);
81 #elif defined(OS_POSIX)
82 channel->PrepareToPassRemoteEndpoint(&options->fds_to_remap, &value);
83 #elif defined(OS_WIN)
84 channel->PrepareToPassRemoteEndpoint(&options->handles_to_inherit, &value);
85 #else
86 #error "Platform not yet supported."
87 #endif
88
89 if (switch_name.empty())
90 switch_name = PlatformChannel::kHandleSwitch;
91 command_line->AppendSwitchASCII(switch_name.as_string(), value);
92 }
93
TEST_F(InvitationTest,Create)94 TEST_F(InvitationTest, Create) {
95 MojoHandle invitation;
96 EXPECT_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation));
97 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
98
99 MojoCreateInvitationOptions options;
100 options.struct_size = sizeof(options);
101 options.flags = MOJO_CREATE_INVITATION_FLAG_NONE;
102 EXPECT_EQ(MOJO_RESULT_OK, MojoCreateInvitation(&options, &invitation));
103 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
104 }
105
TEST_F(InvitationTest,InvalidArguments)106 TEST_F(InvitationTest, InvalidArguments) {
107 MojoHandle invitation;
108 MojoCreateInvitationOptions invalid_create_options;
109 invalid_create_options.struct_size = 0;
110 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
111 MojoCreateInvitation(&invalid_create_options, &invitation));
112 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
113 MojoCreateInvitation(nullptr, nullptr));
114
115 // We need a valid invitation handle to exercise some of the other invalid
116 // argument cases below.
117 EXPECT_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation));
118
119 MojoHandle pipe;
120 MojoAttachMessagePipeToInvitationOptions invalid_attach_options;
121 invalid_attach_options.struct_size = 0;
122 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
123 MojoAttachMessagePipeToInvitation(MOJO_HANDLE_INVALID, "x", 1,
124 nullptr, &pipe));
125 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
126 MojoAttachMessagePipeToInvitation(invitation, "x", 1,
127 &invalid_attach_options, &pipe));
128 EXPECT_EQ(
129 MOJO_RESULT_INVALID_ARGUMENT,
130 MojoAttachMessagePipeToInvitation(invitation, "x", 1, nullptr, nullptr));
131
132 MojoExtractMessagePipeFromInvitationOptions invalid_extract_options;
133 invalid_extract_options.struct_size = 0;
134 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
135 MojoExtractMessagePipeFromInvitation(MOJO_HANDLE_INVALID, "x", 1,
136 nullptr, &pipe));
137 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
138 MojoExtractMessagePipeFromInvitation(
139 invitation, "x", 1, &invalid_extract_options, &pipe));
140 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
141 MojoExtractMessagePipeFromInvitation(invitation, "x", 1, nullptr,
142 nullptr));
143
144 PlatformChannel channel;
145 MojoPlatformHandle endpoint_handle;
146 endpoint_handle.struct_size = sizeof(endpoint_handle);
147 PlatformHandle::ToMojoPlatformHandle(
148 channel.TakeLocalEndpoint().TakePlatformHandle(), &endpoint_handle);
149 ASSERT_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
150
151 MojoInvitationTransportEndpoint valid_endpoint;
152 valid_endpoint.struct_size = sizeof(valid_endpoint);
153 valid_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
154 valid_endpoint.num_platform_handles = 1;
155 valid_endpoint.platform_handles = &endpoint_handle;
156
157 MojoSendInvitationOptions invalid_send_options;
158 invalid_send_options.struct_size = 0;
159 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
160 MojoSendInvitation(MOJO_HANDLE_INVALID, nullptr, &valid_endpoint,
161 nullptr, 0, nullptr));
162 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
163 MojoSendInvitation(invitation, nullptr, &valid_endpoint, nullptr, 0,
164 &invalid_send_options));
165
166 MojoInvitationTransportEndpoint invalid_endpoint;
167 invalid_endpoint.struct_size = 0;
168 invalid_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
169 invalid_endpoint.num_platform_handles = 1;
170 invalid_endpoint.platform_handles = &endpoint_handle;
171 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
172 MojoSendInvitation(invitation, nullptr, &invalid_endpoint, nullptr,
173 0, nullptr));
174
175 invalid_endpoint.struct_size = sizeof(invalid_endpoint);
176 invalid_endpoint.num_platform_handles = 0;
177 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
178 MojoSendInvitation(invitation, nullptr, &invalid_endpoint, nullptr,
179 0, nullptr));
180
181 MojoPlatformHandle invalid_platform_handle;
182 invalid_platform_handle.struct_size = 0;
183 invalid_endpoint.num_platform_handles = 1;
184 invalid_endpoint.platform_handles = &invalid_platform_handle;
185 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
186 MojoSendInvitation(invitation, nullptr, &invalid_endpoint, nullptr,
187 0, nullptr));
188 invalid_platform_handle.struct_size = sizeof(invalid_platform_handle);
189 invalid_platform_handle.type = MOJO_PLATFORM_HANDLE_TYPE_INVALID;
190 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
191 MojoSendInvitation(invitation, nullptr, &invalid_endpoint, nullptr,
192 0, nullptr));
193
194 invalid_endpoint.num_platform_handles = 1;
195 invalid_endpoint.platform_handles = nullptr;
196 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
197 MojoSendInvitation(invitation, nullptr, &invalid_endpoint, nullptr,
198 0, nullptr));
199
200 MojoHandle accepted_invitation;
201 MojoAcceptInvitationOptions invalid_accept_options;
202 invalid_accept_options.struct_size = 0;
203 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
204 MojoAcceptInvitation(nullptr, nullptr, &accepted_invitation));
205 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
206 MojoAcceptInvitation(&valid_endpoint, &invalid_accept_options,
207 &accepted_invitation));
208 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
209 MojoAcceptInvitation(&valid_endpoint, nullptr, nullptr));
210
211 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
212 }
213
TEST_F(InvitationTest,AttachAndExtractLocally)214 TEST_F(InvitationTest, AttachAndExtractLocally) {
215 MojoHandle invitation;
216 EXPECT_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation));
217
218 MojoHandle pipe0 = MOJO_HANDLE_INVALID;
219 EXPECT_EQ(MOJO_RESULT_OK, MojoAttachMessagePipeToInvitation(
220 invitation, "x", 1, nullptr, &pipe0));
221 EXPECT_NE(MOJO_HANDLE_INVALID, pipe0);
222
223 MojoHandle pipe1 = MOJO_HANDLE_INVALID;
224 EXPECT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(
225 invitation, "x", 1, nullptr, &pipe1));
226 EXPECT_NE(MOJO_HANDLE_INVALID, pipe1);
227
228 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
229
230 // Should be able to communicate over the pipe.
231 const std::string kMessage = "RSVP LOL";
232 WriteMessage(pipe0, kMessage);
233 EXPECT_EQ(kMessage, ReadMessage(pipe1));
234
235 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe0));
236 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe1));
237 }
238
TEST_F(InvitationTest,ClosedInvitationClosesAttachments)239 TEST_F(InvitationTest, ClosedInvitationClosesAttachments) {
240 MojoHandle invitation;
241 EXPECT_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation));
242
243 MojoHandle pipe = MOJO_HANDLE_INVALID;
244 EXPECT_EQ(MOJO_RESULT_OK, MojoAttachMessagePipeToInvitation(
245 invitation, "x", 1, nullptr, &pipe));
246 EXPECT_NE(MOJO_HANDLE_INVALID, pipe);
247
248 // Closing the invitation should close |pipe|'s peer.
249 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
250
251 EXPECT_EQ(MOJO_RESULT_OK,
252 WaitForSignals(pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
253 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe));
254 }
255
TEST_F(InvitationTest,AttachNameInUse)256 TEST_F(InvitationTest, AttachNameInUse) {
257 MojoHandle invitation;
258 EXPECT_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation));
259
260 MojoHandle pipe0 = MOJO_HANDLE_INVALID;
261 EXPECT_EQ(MOJO_RESULT_OK, MojoAttachMessagePipeToInvitation(
262 invitation, "x", 1, nullptr, &pipe0));
263 EXPECT_NE(MOJO_HANDLE_INVALID, pipe0);
264
265 MojoHandle pipe1 = MOJO_HANDLE_INVALID;
266 EXPECT_EQ(
267 MOJO_RESULT_ALREADY_EXISTS,
268 MojoAttachMessagePipeToInvitation(invitation, "x", 1, nullptr, &pipe1));
269 EXPECT_EQ(MOJO_HANDLE_INVALID, pipe1);
270 EXPECT_EQ(MOJO_RESULT_OK, MojoAttachMessagePipeToInvitation(
271 invitation, "y", 1, nullptr, &pipe1));
272 EXPECT_NE(MOJO_HANDLE_INVALID, pipe1);
273
274 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
275 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe0));
276 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe1));
277 }
278
279 // static
LaunchChildTestClient(const std::string & test_client_name,MojoHandle * primordial_pipes,size_t num_primordial_pipes,TransportType transport_type,MojoSendInvitationFlags send_flags,MojoProcessErrorHandler error_handler,uintptr_t error_handler_context,base::CommandLine * custom_command_line,base::LaunchOptions * custom_launch_options)280 base::Process InvitationTest::LaunchChildTestClient(
281 const std::string& test_client_name,
282 MojoHandle* primordial_pipes,
283 size_t num_primordial_pipes,
284 TransportType transport_type,
285 MojoSendInvitationFlags send_flags,
286 MojoProcessErrorHandler error_handler,
287 uintptr_t error_handler_context,
288 base::CommandLine* custom_command_line,
289 base::LaunchOptions* custom_launch_options) {
290 base::CommandLine default_command_line =
291 base::GetMultiProcessTestChildBaseCommandLine();
292 base::CommandLine& command_line =
293 custom_command_line ? *custom_command_line : default_command_line;
294
295 base::LaunchOptions default_launch_options;
296 base::LaunchOptions& launch_options =
297 custom_launch_options ? *custom_launch_options : default_launch_options;
298 #if defined(OS_WIN)
299 launch_options.start_hidden = true;
300 #endif
301
302 base::Optional<PlatformChannel> channel;
303 base::Optional<NamedPlatformChannel> named_channel;
304 PlatformHandle local_endpoint_handle;
305 if (transport_type == TransportType::kChannel) {
306 channel.emplace();
307 PrepareToPassRemoteEndpoint(&channel.value(), &launch_options,
308 &command_line);
309 local_endpoint_handle = channel->TakeLocalEndpoint().TakePlatformHandle();
310 } else {
311 #if defined(OS_FUCHSIA)
312 NOTREACHED() << "Named pipe support does not exist for Mojo on Fuchsia.";
313 #else
314 NamedPlatformChannel::Options named_channel_options;
315 #if !defined(OS_WIN)
316 CHECK(base::PathService::Get(base::DIR_TEMP,
317 &named_channel_options.socket_dir));
318 #endif
319 named_channel.emplace(named_channel_options);
320 named_channel->PassServerNameOnCommandLine(&command_line);
321 local_endpoint_handle =
322 named_channel->TakeServerEndpoint().TakePlatformHandle();
323 #endif
324 }
325
326 base::Process child_process = base::SpawnMultiProcessTestChild(
327 test_client_name, command_line, launch_options);
328 if (channel)
329 channel->RemoteProcessLaunchAttempted();
330
331 SendInvitationToClient(std::move(local_endpoint_handle),
332 child_process.Handle(), primordial_pipes,
333 num_primordial_pipes, transport_type, send_flags,
334 error_handler, error_handler_context, "");
335
336 return child_process;
337 }
338
339 // static
SendInvitationToClient(PlatformHandle endpoint_handle,base::ProcessHandle process,MojoHandle * primordial_pipes,size_t num_primordial_pipes,TransportType transport_type,MojoSendInvitationFlags flags,MojoProcessErrorHandler error_handler,uintptr_t error_handler_context,base::StringPiece isolated_invitation_name)340 void InvitationTest::SendInvitationToClient(
341 PlatformHandle endpoint_handle,
342 base::ProcessHandle process,
343 MojoHandle* primordial_pipes,
344 size_t num_primordial_pipes,
345 TransportType transport_type,
346 MojoSendInvitationFlags flags,
347 MojoProcessErrorHandler error_handler,
348 uintptr_t error_handler_context,
349 base::StringPiece isolated_invitation_name) {
350 MojoPlatformHandle handle;
351 PlatformHandle::ToMojoPlatformHandle(std::move(endpoint_handle), &handle);
352 CHECK_NE(handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
353
354 MojoHandle invitation;
355 CHECK_EQ(MOJO_RESULT_OK, MojoCreateInvitation(nullptr, &invitation));
356 for (uint32_t name = 0; name < num_primordial_pipes; ++name) {
357 CHECK_EQ(MOJO_RESULT_OK,
358 MojoAttachMessagePipeToInvitation(invitation, &name, 4, nullptr,
359 &primordial_pipes[name]));
360 }
361
362 MojoPlatformProcessHandle process_handle;
363 process_handle.struct_size = sizeof(process_handle);
364 #if defined(OS_WIN)
365 process_handle.value =
366 static_cast<uint64_t>(reinterpret_cast<uintptr_t>(process));
367 #else
368 process_handle.value = static_cast<uint64_t>(process);
369 #endif
370
371 MojoInvitationTransportEndpoint transport_endpoint;
372 transport_endpoint.struct_size = sizeof(transport_endpoint);
373 if (transport_type == TransportType::kChannel)
374 transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
375 else
376 transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL_SERVER;
377 transport_endpoint.num_platform_handles = 1;
378 transport_endpoint.platform_handles = &handle;
379
380 MojoSendInvitationOptions options;
381 options.struct_size = sizeof(options);
382 options.flags = flags;
383 if (flags & MOJO_SEND_INVITATION_FLAG_ISOLATED) {
384 options.isolated_connection_name = isolated_invitation_name.data();
385 options.isolated_connection_name_length =
386 static_cast<uint32_t>(isolated_invitation_name.size());
387 }
388 CHECK_EQ(MOJO_RESULT_OK,
389 MojoSendInvitation(invitation, &process_handle, &transport_endpoint,
390 error_handler, error_handler_context, &options));
391 }
392
393 class TestClientBase : public InvitationTest {
394 public:
AcceptInvitation(MojoAcceptInvitationFlags flags,base::StringPiece switch_name={})395 static MojoHandle AcceptInvitation(MojoAcceptInvitationFlags flags,
396 base::StringPiece switch_name = {}) {
397 const auto& command_line = *base::CommandLine::ForCurrentProcess();
398 PlatformChannelEndpoint channel_endpoint =
399 NamedPlatformChannel::ConnectToServer(command_line);
400 if (!channel_endpoint.is_valid()) {
401 if (switch_name.empty()) {
402 channel_endpoint =
403 PlatformChannel::RecoverPassedEndpointFromCommandLine(command_line);
404 } else {
405 channel_endpoint = PlatformChannel::RecoverPassedEndpointFromString(
406 command_line.GetSwitchValueASCII(switch_name));
407 }
408 }
409 MojoPlatformHandle endpoint_handle;
410 PlatformHandle::ToMojoPlatformHandle(channel_endpoint.TakePlatformHandle(),
411 &endpoint_handle);
412 CHECK_NE(endpoint_handle.type, MOJO_PLATFORM_HANDLE_TYPE_INVALID);
413
414 MojoInvitationTransportEndpoint transport_endpoint;
415 transport_endpoint.struct_size = sizeof(transport_endpoint);
416 transport_endpoint.type = MOJO_INVITATION_TRANSPORT_TYPE_CHANNEL;
417 transport_endpoint.num_platform_handles = 1;
418 transport_endpoint.platform_handles = &endpoint_handle;
419
420 MojoAcceptInvitationOptions options;
421 options.struct_size = sizeof(options);
422 options.flags = flags;
423 MojoHandle invitation;
424 CHECK_EQ(MOJO_RESULT_OK,
425 MojoAcceptInvitation(&transport_endpoint, &options, &invitation));
426 return invitation;
427 }
428
429 private:
430 DISALLOW_COPY_AND_ASSIGN(TestClientBase);
431 };
432
433 #define DEFINE_TEST_CLIENT(name) \
434 class name##Impl : public TestClientBase { \
435 public: \
436 static void Run(); \
437 }; \
438 MULTIPROCESS_TEST_MAIN(name) { \
439 name##Impl::Run(); \
440 return 0; \
441 } \
442 void name##Impl::Run()
443
444 const std::string kTestMessage1 = "i am the pusher robot";
445 const std::string kTestMessage2 = "i push the messages down the pipe";
446 const std::string kTestMessage3 = "i am the shover robot";
447 const std::string kTestMessage4 = "i shove the messages down the pipe";
448
TEST_F(InvitationTest,SendInvitation)449 TEST_F(InvitationTest, SendInvitation) {
450 MojoHandle primordial_pipe;
451 base::Process child_process = LaunchChildTestClient(
452 "SendInvitationClient", &primordial_pipe, 1, TransportType::kChannel,
453 MOJO_SEND_INVITATION_FLAG_NONE);
454
455 WriteMessage(primordial_pipe, kTestMessage1);
456 EXPECT_EQ(MOJO_RESULT_OK,
457 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE));
458 EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe));
459 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
460
461 int wait_result = -1;
462 base::WaitForMultiprocessTestChildExit(
463 child_process, TestTimeouts::action_timeout(), &wait_result);
464 child_process.Close();
465 EXPECT_EQ(0, wait_result);
466 }
467
DEFINE_TEST_CLIENT(SendInvitationClient)468 DEFINE_TEST_CLIENT(SendInvitationClient) {
469 MojoHandle primordial_pipe;
470 MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
471 const uint32_t pipe_name = 0;
472 ASSERT_EQ(MOJO_RESULT_OK,
473 MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
474 nullptr, &primordial_pipe));
475 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
476
477 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
478 ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
479 WriteMessage(primordial_pipe, kTestMessage3);
480 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
481
482 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
483 }
484
TEST_F(InvitationTest,SendInvitationMultiplePipes)485 TEST_F(InvitationTest, SendInvitationMultiplePipes) {
486 MojoHandle pipes[2];
487 base::Process child_process = LaunchChildTestClient(
488 "SendInvitationMultiplePipesClient", pipes, 2, TransportType::kChannel,
489 MOJO_SEND_INVITATION_FLAG_NONE);
490
491 WriteMessage(pipes[0], kTestMessage1);
492 WriteMessage(pipes[1], kTestMessage2);
493 EXPECT_EQ(MOJO_RESULT_OK,
494 WaitForSignals(pipes[0], MOJO_HANDLE_SIGNAL_READABLE));
495 EXPECT_EQ(MOJO_RESULT_OK,
496 WaitForSignals(pipes[1], MOJO_HANDLE_SIGNAL_READABLE));
497 EXPECT_EQ(kTestMessage3, ReadMessage(pipes[0]));
498 EXPECT_EQ(kTestMessage4, ReadMessage(pipes[1]));
499
500 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipes[0]));
501 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(pipes[1]));
502
503 int wait_result = -1;
504 base::WaitForMultiprocessTestChildExit(
505 child_process, TestTimeouts::action_timeout(), &wait_result);
506 child_process.Close();
507 EXPECT_EQ(0, wait_result);
508 }
509
DEFINE_TEST_CLIENT(SendInvitationMultiplePipesClient)510 DEFINE_TEST_CLIENT(SendInvitationMultiplePipesClient) {
511 MojoHandle pipes[2];
512 MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
513 const uint32_t pipe_names[] = {0, 1};
514 ASSERT_EQ(MOJO_RESULT_OK,
515 MojoExtractMessagePipeFromInvitation(invitation, &pipe_names[0], 4,
516 nullptr, &pipes[0]));
517 ASSERT_EQ(MOJO_RESULT_OK,
518 MojoExtractMessagePipeFromInvitation(invitation, &pipe_names[1], 4,
519 nullptr, &pipes[1]));
520
521 WaitForSignals(pipes[0], MOJO_HANDLE_SIGNAL_READABLE);
522 WaitForSignals(pipes[1], MOJO_HANDLE_SIGNAL_READABLE);
523 ASSERT_EQ(kTestMessage1, ReadMessage(pipes[0]));
524 ASSERT_EQ(kTestMessage2, ReadMessage(pipes[1]));
525 WriteMessage(pipes[0], kTestMessage3);
526 WriteMessage(pipes[1], kTestMessage4);
527 WaitForSignals(pipes[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED);
528 WaitForSignals(pipes[1], MOJO_HANDLE_SIGNAL_PEER_CLOSED);
529 }
530
531 #if !defined(OS_FUCHSIA)
TEST_F(InvitationTest,SendInvitationWithServer)532 TEST_F(InvitationTest, SendInvitationWithServer) {
533 MojoHandle primordial_pipe;
534 base::Process child_process = LaunchChildTestClient(
535 "SendInvitationWithServerClient", &primordial_pipe, 1,
536 TransportType::kChannelServer, MOJO_SEND_INVITATION_FLAG_NONE);
537
538 WriteMessage(primordial_pipe, kTestMessage1);
539 EXPECT_EQ(MOJO_RESULT_OK,
540 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE));
541 EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe));
542 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
543
544 int wait_result = -1;
545 base::WaitForMultiprocessTestChildExit(
546 child_process, TestTimeouts::action_timeout(), &wait_result);
547 child_process.Close();
548 EXPECT_EQ(0, wait_result);
549 }
550
DEFINE_TEST_CLIENT(SendInvitationWithServerClient)551 DEFINE_TEST_CLIENT(SendInvitationWithServerClient) {
552 MojoHandle primordial_pipe;
553 MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
554 const uint32_t pipe_name = 0;
555 ASSERT_EQ(MOJO_RESULT_OK,
556 MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
557 nullptr, &primordial_pipe));
558 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
559
560 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
561 ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
562 WriteMessage(primordial_pipe, kTestMessage3);
563 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
564
565 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
566 }
567 #endif // !defined(OS_FUCHSIA)
568
569 const char kErrorMessage[] = "ur bad :(";
570 const char kDisconnectMessage[] = "go away plz";
571
572 class RemoteProcessState {
573 public:
RemoteProcessState()574 RemoteProcessState()
575 : callback_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
576 ~RemoteProcessState() = default;
577
disconnected()578 bool disconnected() {
579 base::AutoLock lock(lock_);
580 return disconnected_;
581 }
582
set_error_callback(base::RepeatingClosure callback)583 void set_error_callback(base::RepeatingClosure callback) {
584 error_callback_ = std::move(callback);
585 }
586
set_expected_error_message(const std::string & expected)587 void set_expected_error_message(const std::string& expected) {
588 expected_error_message_ = expected;
589 }
590
NotifyError(const std::string & error_message,bool disconnected)591 void NotifyError(const std::string& error_message, bool disconnected) {
592 base::AutoLock lock(lock_);
593 CHECK(!disconnected_);
594 EXPECT_NE(error_message.find(expected_error_message_), std::string::npos);
595 disconnected_ = disconnected;
596 ++call_count_;
597 if (error_callback_)
598 callback_task_runner_->PostTask(FROM_HERE, error_callback_);
599 }
600
601 private:
602 const scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
603
604 base::Lock lock_;
605 int call_count_ = 0;
606 bool disconnected_ = false;
607 std::string expected_error_message_;
608 base::RepeatingClosure error_callback_;
609
610 DISALLOW_COPY_AND_ASSIGN(RemoteProcessState);
611 };
612
TestProcessErrorHandler(uintptr_t context,const MojoProcessErrorDetails * details)613 void TestProcessErrorHandler(uintptr_t context,
614 const MojoProcessErrorDetails* details) {
615 auto* state = reinterpret_cast<RemoteProcessState*>(context);
616 std::string error_message;
617 if (details->error_message) {
618 error_message =
619 std::string(details->error_message, details->error_message_length - 1);
620 }
621 state->NotifyError(error_message,
622 details->flags & MOJO_PROCESS_ERROR_FLAG_DISCONNECTED);
623 }
624
TEST_F(InvitationTest,ProcessErrors)625 TEST_F(InvitationTest, ProcessErrors) {
626 RemoteProcessState process_state;
627 MojoHandle pipe;
628 base::Process child_process = LaunchChildTestClient(
629 "ProcessErrorsClient", &pipe, 1, TransportType::kChannel,
630 MOJO_SEND_INVITATION_FLAG_NONE, &TestProcessErrorHandler,
631 reinterpret_cast<uintptr_t>(&process_state));
632
633 MojoMessageHandle message;
634 WaitForSignals(pipe, MOJO_HANDLE_SIGNAL_READABLE);
635 EXPECT_EQ(MOJO_RESULT_OK, MojoReadMessage(pipe, nullptr, &message));
636
637 base::RunLoop error_loop;
638 process_state.set_error_callback(error_loop.QuitClosure());
639
640 // Report this message as "bad". This should cause the error handler to be
641 // invoked and the RunLoop to be quit.
642 process_state.set_expected_error_message(kErrorMessage);
643 EXPECT_EQ(MOJO_RESULT_OK,
644 MojoNotifyBadMessage(message, kErrorMessage, sizeof(kErrorMessage),
645 nullptr));
646 error_loop.Run();
647 EXPECT_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message));
648
649 // Now tell the child it can exit, and wait for it to disconnect.
650 base::RunLoop disconnect_loop;
651 process_state.set_error_callback(disconnect_loop.QuitClosure());
652 process_state.set_expected_error_message(std::string());
653 WriteMessage(pipe, kDisconnectMessage);
654 disconnect_loop.Run();
655
656 EXPECT_TRUE(process_state.disconnected());
657
658 int wait_result = -1;
659 base::WaitForMultiprocessTestChildExit(
660 child_process, TestTimeouts::action_timeout(), &wait_result);
661 child_process.Close();
662 EXPECT_EQ(0, wait_result);
663 }
664
DEFINE_TEST_CLIENT(ProcessErrorsClient)665 DEFINE_TEST_CLIENT(ProcessErrorsClient) {
666 MojoHandle pipe;
667 MojoHandle invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_NONE);
668 const uint32_t pipe_name = 0;
669 ASSERT_EQ(MOJO_RESULT_OK, MojoExtractMessagePipeFromInvitation(
670 invitation, &pipe_name, 4, nullptr, &pipe));
671 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
672
673 // Send a message. Contents are irrelevant, the test process is just going to
674 // flag it as a bad.
675 WriteMessage(pipe, "doesn't matter");
676
677 // Wait for our goodbye before exiting.
678 WaitForSignals(pipe, MOJO_HANDLE_SIGNAL_READABLE);
679 EXPECT_EQ(kDisconnectMessage, ReadMessage(pipe));
680 }
681
TEST_F(InvitationTest,SendIsolatedInvitation)682 TEST_F(InvitationTest, SendIsolatedInvitation) {
683 MojoHandle primordial_pipe;
684 base::Process child_process = LaunchChildTestClient(
685 "SendIsolatedInvitationClient", &primordial_pipe, 1,
686 TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED);
687
688 WriteMessage(primordial_pipe, kTestMessage1);
689 EXPECT_EQ(MOJO_RESULT_OK,
690 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE));
691 EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe));
692 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
693
694 int wait_result = -1;
695 base::WaitForMultiprocessTestChildExit(
696 child_process, TestTimeouts::action_timeout(), &wait_result);
697 child_process.Close();
698 EXPECT_EQ(0, wait_result);
699 }
700
DEFINE_TEST_CLIENT(SendIsolatedInvitationClient)701 DEFINE_TEST_CLIENT(SendIsolatedInvitationClient) {
702 MojoHandle primordial_pipe;
703 MojoHandle invitation =
704 AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED);
705 const uint32_t pipe_name = 0;
706 ASSERT_EQ(MOJO_RESULT_OK,
707 MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
708 nullptr, &primordial_pipe));
709 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
710
711 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
712 ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
713 WriteMessage(primordial_pipe, kTestMessage3);
714 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
715
716 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
717 }
718
TEST_F(InvitationTest,SendMultipleIsolatedInvitations)719 TEST_F(InvitationTest, SendMultipleIsolatedInvitations) {
720 // We send a secondary transport to the client process so we can send a second
721 // isolated invitation.
722 base::CommandLine command_line =
723 base::GetMultiProcessTestChildBaseCommandLine();
724 PlatformChannel secondary_transport;
725 base::LaunchOptions options;
726 PrepareToPassRemoteEndpoint(&secondary_transport, &options, &command_line,
727 kSecondaryChannelHandleSwitch);
728
729 MojoHandle primordial_pipe;
730 base::Process child_process = LaunchChildTestClient(
731 "SendMultipleIsolatedInvitationsClient", &primordial_pipe, 1,
732 TransportType::kChannel, MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0,
733 &command_line, &options);
734 secondary_transport.RemoteProcessLaunchAttempted();
735
736 WriteMessage(primordial_pipe, kTestMessage1);
737 EXPECT_EQ(MOJO_RESULT_OK,
738 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE));
739 EXPECT_EQ(kTestMessage3, ReadMessage(primordial_pipe));
740
741 // Send another invitation over our seconary pipe. This should trample the
742 // original connection, breaking the first pipe.
743 MojoHandle new_pipe;
744 SendInvitationToClient(
745 secondary_transport.TakeLocalEndpoint().TakePlatformHandle(),
746 child_process.Handle(), &new_pipe, 1, TransportType::kChannel,
747 MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, "");
748 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
749 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
750
751 // And the new pipe should be working.
752 WriteMessage(new_pipe, kTestMessage1);
753 EXPECT_EQ(MOJO_RESULT_OK,
754 WaitForSignals(new_pipe, MOJO_HANDLE_SIGNAL_READABLE));
755 EXPECT_EQ(kTestMessage3, ReadMessage(new_pipe));
756 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(new_pipe));
757
758 int wait_result = -1;
759 base::WaitForMultiprocessTestChildExit(
760 child_process, TestTimeouts::action_timeout(), &wait_result);
761 child_process.Close();
762 EXPECT_EQ(0, wait_result);
763 }
764
DEFINE_TEST_CLIENT(SendMultipleIsolatedInvitationsClient)765 DEFINE_TEST_CLIENT(SendMultipleIsolatedInvitationsClient) {
766 MojoHandle primordial_pipe;
767 MojoHandle invitation =
768 AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED);
769 const uint32_t pipe_name = 0;
770 ASSERT_EQ(MOJO_RESULT_OK,
771 MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
772 nullptr, &primordial_pipe));
773 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
774
775 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
776 ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
777 WriteMessage(primordial_pipe, kTestMessage3);
778
779 // The above pipe should get closed once we accept a new invitation.
780 invitation = AcceptInvitation(MOJO_ACCEPT_INVITATION_FLAG_ISOLATED,
781 kSecondaryChannelHandleSwitch);
782 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
783 primordial_pipe = MOJO_HANDLE_INVALID;
784 ASSERT_EQ(MOJO_RESULT_OK,
785 MojoExtractMessagePipeFromInvitation(invitation, &pipe_name, 4,
786 nullptr, &primordial_pipe));
787 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(invitation));
788 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_READABLE);
789 ASSERT_EQ(kTestMessage1, ReadMessage(primordial_pipe));
790 WriteMessage(primordial_pipe, kTestMessage3);
791 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
792
793 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
794 }
795
TEST_F(InvitationTest,SendIsolatedInvitationWithDuplicateName)796 TEST_F(InvitationTest, SendIsolatedInvitationWithDuplicateName) {
797 PlatformChannel channel1;
798 PlatformChannel channel2;
799 MojoHandle pipe0, pipe1;
800 const char kConnectionName[] = "there can be only one!";
801 SendInvitationToClient(
802 channel1.TakeLocalEndpoint().TakePlatformHandle(),
803 base::kNullProcessHandle, &pipe0, 1, TransportType::kChannel,
804 MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName);
805
806 // Send another invitation with the same connection name. |pipe0| should be
807 // disconnected as the first invitation's connection is torn down.
808 SendInvitationToClient(
809 channel2.TakeLocalEndpoint().TakePlatformHandle(),
810 base::kNullProcessHandle, &pipe1, 1, TransportType::kChannel,
811 MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, kConnectionName);
812
813 WaitForSignals(pipe0, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
814 }
815
TEST_F(InvitationTest,SendIsolatedInvitationToSelf)816 TEST_F(InvitationTest, SendIsolatedInvitationToSelf) {
817 PlatformChannel channel;
818 MojoHandle pipe0, pipe1;
819 SendInvitationToClient(channel.TakeLocalEndpoint().TakePlatformHandle(),
820 base::kNullProcessHandle, &pipe0, 1,
821 TransportType::kChannel,
822 MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, "");
823 SendInvitationToClient(channel.TakeRemoteEndpoint().TakePlatformHandle(),
824 base::kNullProcessHandle, &pipe1, 1,
825 TransportType::kChannel,
826 MOJO_SEND_INVITATION_FLAG_ISOLATED, nullptr, 0, "");
827
828 WriteMessage(pipe0, kTestMessage1);
829 EXPECT_EQ(kTestMessage1, ReadMessage(pipe1));
830 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe0));
831 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe1));
832 }
833
TEST_F(InvitationTest,BrokenInvitationTransportBreaksAttachedPipe)834 TEST_F(InvitationTest, BrokenInvitationTransportBreaksAttachedPipe) {
835 MojoHandle primordial_pipe;
836 base::Process child_process = LaunchChildTestClient(
837 "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel,
838 MOJO_SEND_INVITATION_FLAG_NONE);
839
840 EXPECT_EQ(MOJO_RESULT_OK,
841 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
842 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
843
844 int wait_result = -1;
845 base::WaitForMultiprocessTestChildExit(
846 child_process, TestTimeouts::action_timeout(), &wait_result);
847 child_process.Close();
848 EXPECT_EQ(0, wait_result);
849 }
850
TEST_F(InvitationTest,BrokenIsolatedInvitationTransportBreaksAttachedPipe)851 TEST_F(InvitationTest, BrokenIsolatedInvitationTransportBreaksAttachedPipe) {
852 MojoHandle primordial_pipe;
853 base::Process child_process = LaunchChildTestClient(
854 "BrokenTransportClient", &primordial_pipe, 1, TransportType::kChannel,
855 MOJO_SEND_INVITATION_FLAG_ISOLATED);
856
857 EXPECT_EQ(MOJO_RESULT_OK,
858 WaitForSignals(primordial_pipe, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
859 EXPECT_EQ(MOJO_RESULT_OK, MojoClose(primordial_pipe));
860
861 int wait_result = -1;
862 base::WaitForMultiprocessTestChildExit(
863 child_process, TestTimeouts::action_timeout(), &wait_result);
864 child_process.Close();
865 EXPECT_EQ(0, wait_result);
866 }
867
DEFINE_TEST_CLIENT(BrokenTransportClient)868 DEFINE_TEST_CLIENT(BrokenTransportClient) {
869 // No-op. Exit immediately without accepting any invitation.
870 }
871
872 } // namespace
873 } // namespace core
874 } // namespace mojo
875