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.
5 #include "sandbox/mac/xpc_message_server.h"
9 #include <servers/bootstrap.h>
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/mac/mac_util.h"
14 #include "base/mac/scoped_mach_port.h"
15 #include "base/process/kill.h"
16 #include "base/test/multiprocess_test.h"
17 #include "sandbox/mac/xpc.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "testing/multiprocess_func_list.h"
23 class XPCMessageServerTest
: public testing::Test
{
25 void SetUp() override
{
28 ASSERT_TRUE(InitializeXPC());
32 return base::mac::IsOSMountainLionOrLater();
36 // A MessageDemuxer that manages a test server and executes a block for every
38 class BlockDemuxer
: public MessageDemuxer
{
42 server_(this, MACH_PORT_NULL
),
46 ~BlockDemuxer() override
{
50 Block_release(demux_block_
);
53 // Starts running the server, given a block to handle incoming IPC messages.
54 bool Initialize(void (^demux_block
)(IPCMessage request
)) {
55 if (!server_
.Initialize())
58 // Create a send right on the port so that the XPC pipe can be created.
59 if (mach_port_insert_right(mach_task_self(), server_
.GetServerPort(),
60 server_
.GetServerPort(), MACH_MSG_TYPE_MAKE_SEND
) != KERN_SUCCESS
) {
63 scoped_send_right_
.reset(server_
.GetServerPort());
65 demux_block_
= Block_copy(demux_block
);
66 pipe_
= xpc_pipe_create_from_port(server_
.GetServerPort(), 0);
71 void DemuxMessage(IPCMessage request
) override
{
72 demux_block_(request
);
75 xpc_pipe_t
pipe() { return pipe_
; }
77 XPCMessageServer
* server() { return &server_
; }
80 void (^demux_block_
)(IPCMessage request
);
82 XPCMessageServer server_
;
84 base::mac::ScopedMachSendRight scoped_send_right_
;
89 #define XPC_TEST_F(name) TEST_F(XPCMessageServerTest, name) { \
93 XPC_TEST_F(ReceiveMessage) // {
95 XPCMessageServer
* server
= fixture
.server();
97 uint64_t __block value
= 0;
98 ASSERT_TRUE(fixture
.Initialize(^(IPCMessage request
) {
99 value
= xpc_dictionary_get_uint64(request
.xpc
, "test_value");
100 server
->SendReply(server
->CreateReply(request
));
103 xpc_object_t request
= xpc_dictionary_create(NULL
, NULL
, 0);
104 xpc_dictionary_set_uint64(request
, "test_value", 42);
107 EXPECT_EQ(0, xpc_pipe_routine(fixture
.pipe(), request
, &reply
));
109 EXPECT_EQ(42u, value
);
111 xpc_release(request
);
115 XPC_TEST_F(RejectMessage
) // {
116 BlockDemuxer fixture
;
117 XPCMessageServer
* server
= fixture
.server();
118 ASSERT_TRUE(fixture
.Initialize(^(IPCMessage request
) {
119 server
->RejectMessage(request
, EPERM
);
122 xpc_object_t request
= xpc_dictionary_create(NULL
, NULL
, 0);
124 EXPECT_EQ(0, xpc_pipe_routine(fixture
.pipe(), request
, &reply
));
126 EXPECT_EQ(EPERM
, xpc_dictionary_get_int64(reply
, "error"));
128 xpc_release(request
);
132 XPC_TEST_F(RejectMessageSimpleRoutine
) // {
133 BlockDemuxer fixture
;
134 XPCMessageServer
* server
= fixture
.server();
135 ASSERT_TRUE(fixture
.Initialize(^(IPCMessage request
) {
136 server
->RejectMessage(request
, -99);
139 // Create a message that is not expecting a reply.
140 xpc_object_t request
= xpc_dictionary_create(NULL
, NULL
, 0);
141 EXPECT_EQ(0, xpc_pipe_simpleroutine(fixture
.pipe(), request
));
143 xpc_release(request
);
146 char kGetSenderPID
[] = "org.chromium.sandbox.test.GetSenderPID";
148 XPC_TEST_F(GetSenderPID
) // {
149 BlockDemuxer fixture
;
150 XPCMessageServer
* server
= fixture
.server();
152 pid_t __block sender_pid
= 0;
153 int64_t __block child_pid
= 0;
154 ASSERT_TRUE(fixture
.Initialize(^(IPCMessage request
) {
155 sender_pid
= server
->GetMessageSenderPID(request
);
156 child_pid
= xpc_dictionary_get_int64(request
.xpc
, "child_pid");
159 #pragma GCC diagnostic push
160 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
161 kern_return_t kr
= bootstrap_register(bootstrap_port
, kGetSenderPID
,
162 server
->GetServerPort());
163 #pragma GCC diagnostic pop
164 ASSERT_EQ(KERN_SUCCESS
, kr
);
166 base::Process child
= base::SpawnMultiProcessTestChild(
168 base::GetMultiProcessTestChildBaseCommandLine(),
169 base::LaunchOptions());
170 ASSERT_TRUE(child
.IsValid());
173 ASSERT_TRUE(child
.WaitForExit(&exit_code
));
174 EXPECT_EQ(0, exit_code
);
176 EXPECT_EQ(child
.Pid(), sender_pid
);
177 EXPECT_EQ(child
.Pid(), child_pid
);
178 EXPECT_EQ(sender_pid
, child_pid
);
181 MULTIPROCESS_TEST_MAIN(GetSenderPID
) {
182 CHECK(sandbox::InitializeXPC());
184 mach_port_t port
= MACH_PORT_NULL
;
185 CHECK_EQ(KERN_SUCCESS
, bootstrap_look_up(bootstrap_port
, kGetSenderPID
,
187 base::mac::ScopedMachSendRight
scoped_port(port
);
189 xpc_pipe_t pipe
= xpc_pipe_create_from_port(port
, 0);
191 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
192 xpc_dictionary_set_int64(message
, "child_pid", getpid());
193 CHECK_EQ(0, xpc_pipe_simpleroutine(pipe
, message
));
195 xpc_release(message
);
201 XPC_TEST_F(ForwardMessage
) // {
203 XPCMessageServer
* first_server
= first
.server();
206 XPCMessageServer
* second_server
= second
.server();
208 ASSERT_TRUE(first
.Initialize(^(IPCMessage request
) {
209 xpc_dictionary_set_int64(request
.xpc
, "seen_by_first", 1);
210 first_server
->ForwardMessage(request
, second_server
->GetServerPort());
212 ASSERT_TRUE(second
.Initialize(^(IPCMessage request
) {
213 IPCMessage reply
= second_server
->CreateReply(request
);
214 xpc_dictionary_set_int64(reply
.xpc
, "seen_by_first",
215 xpc_dictionary_get_int64(request
.xpc
, "seen_by_first"));
216 xpc_dictionary_set_int64(reply
.xpc
, "seen_by_second", 2);
217 second_server
->SendReply(reply
);
220 xpc_object_t request
= xpc_dictionary_create(NULL
, NULL
, 0);
222 ASSERT_EQ(0, xpc_pipe_routine(first
.pipe(), request
, &reply
));
224 EXPECT_EQ(1, xpc_dictionary_get_int64(reply
, "seen_by_first"));
225 EXPECT_EQ(2, xpc_dictionary_get_int64(reply
, "seen_by_second"));
227 xpc_release(request
);
231 } // namespace sandbox