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"
21 #if defined(MAC_OS_X_VERSION_10_7) && \
22 MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
23 // Redeclare methods that only exist on 10.7+ to suppress
24 // -Wpartial-availability warnings.
26 XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL_ALL
int64_t
27 xpc_dictionary_get_int64(xpc_object_t xdict
, const char* key
);
29 XPC_EXPORT XPC_WARN_RESULT XPC_NONNULL_ALL
uint64_t
30 xpc_dictionary_get_uint64(xpc_object_t xdict
, const char* key
);
32 XPC_EXPORT XPC_NONNULL1 XPC_NONNULL2
void
33 xpc_dictionary_set_uint64(xpc_object_t xdict
, const char* key
, uint64_t value
);
35 XPC_EXPORT XPC_MALLOC XPC_RETURNS_RETAINED XPC_WARN_RESULT xpc_object_t
36 xpc_dictionary_create(const char* const* keys
,
37 const xpc_object_t
* values
,
44 class XPCMessageServerTest
: public testing::Test
{
46 void SetUp() override
{
49 ASSERT_TRUE(InitializeXPC());
53 return base::mac::IsOSMountainLionOrLater();
57 // A MessageDemuxer that manages a test server and executes a block for every
59 class BlockDemuxer
: public MessageDemuxer
{
63 server_(this, MACH_PORT_NULL
),
67 ~BlockDemuxer() override
{
71 Block_release(demux_block_
);
74 // Starts running the server, given a block to handle incoming IPC messages.
75 bool Initialize(void (^demux_block
)(IPCMessage request
)) {
76 if (!server_
.Initialize())
79 // Create a send right on the port so that the XPC pipe can be created.
80 if (mach_port_insert_right(mach_task_self(), server_
.GetServerPort(),
81 server_
.GetServerPort(), MACH_MSG_TYPE_MAKE_SEND
) != KERN_SUCCESS
) {
84 scoped_send_right_
.reset(server_
.GetServerPort());
86 demux_block_
= Block_copy(demux_block
);
87 pipe_
= xpc_pipe_create_from_port(server_
.GetServerPort(), 0);
92 void DemuxMessage(IPCMessage request
) override
{
93 demux_block_(request
);
96 xpc_pipe_t
pipe() { return pipe_
; }
98 XPCMessageServer
* server() { return &server_
; }
101 void (^demux_block_
)(IPCMessage request
);
103 XPCMessageServer server_
;
105 base::mac::ScopedMachSendRight scoped_send_right_
;
110 #define XPC_TEST_F(name) TEST_F(XPCMessageServerTest, name) { \
114 XPC_TEST_F(ReceiveMessage) // {
115 BlockDemuxer fixture
;
116 XPCMessageServer
* server
= fixture
.server();
118 uint64_t __block value
= 0;
119 ASSERT_TRUE(fixture
.Initialize(^(IPCMessage request
) {
120 value
= xpc_dictionary_get_uint64(request
.xpc
, "test_value");
121 server
->SendReply(server
->CreateReply(request
));
124 xpc_object_t request
= xpc_dictionary_create(NULL
, NULL
, 0);
125 xpc_dictionary_set_uint64(request
, "test_value", 42);
128 EXPECT_EQ(0, xpc_pipe_routine(fixture
.pipe(), request
, &reply
));
130 EXPECT_EQ(42u, value
);
132 xpc_release(request
);
136 XPC_TEST_F(RejectMessage
) // {
137 BlockDemuxer fixture
;
138 XPCMessageServer
* server
= fixture
.server();
139 ASSERT_TRUE(fixture
.Initialize(^(IPCMessage request
) {
140 server
->RejectMessage(request
, EPERM
);
143 xpc_object_t request
= xpc_dictionary_create(NULL
, NULL
, 0);
145 EXPECT_EQ(0, xpc_pipe_routine(fixture
.pipe(), request
, &reply
));
147 EXPECT_EQ(EPERM
, xpc_dictionary_get_int64(reply
, "error"));
149 xpc_release(request
);
153 char kGetSenderPID
[] = "org.chromium.sandbox.test.GetSenderPID";
155 XPC_TEST_F(GetSenderPID
) // {
156 BlockDemuxer fixture
;
157 XPCMessageServer
* server
= fixture
.server();
159 pid_t __block sender_pid
= 0;
160 int64_t __block child_pid
= 0;
161 ASSERT_TRUE(fixture
.Initialize(^(IPCMessage request
) {
162 sender_pid
= server
->GetMessageSenderPID(request
);
163 child_pid
= xpc_dictionary_get_int64(request
.xpc
, "child_pid");
166 #pragma GCC diagnostic push
167 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
168 kern_return_t kr
= bootstrap_register(bootstrap_port
, kGetSenderPID
,
169 server
->GetServerPort());
170 #pragma GCC diagnostic pop
171 ASSERT_EQ(KERN_SUCCESS
, kr
);
173 base::Process child
= base::SpawnMultiProcessTestChild(
175 base::GetMultiProcessTestChildBaseCommandLine(),
176 base::LaunchOptions());
177 ASSERT_TRUE(child
.IsValid());
180 ASSERT_TRUE(child
.WaitForExit(&exit_code
));
181 EXPECT_EQ(0, exit_code
);
183 EXPECT_EQ(child
.Pid(), sender_pid
);
184 EXPECT_EQ(child
.Pid(), child_pid
);
185 EXPECT_EQ(sender_pid
, child_pid
);
188 MULTIPROCESS_TEST_MAIN(GetSenderPID
) {
189 CHECK(sandbox::InitializeXPC());
191 mach_port_t port
= MACH_PORT_NULL
;
192 CHECK_EQ(KERN_SUCCESS
, bootstrap_look_up(bootstrap_port
, kGetSenderPID
,
194 base::mac::ScopedMachSendRight
scoped_port(port
);
196 xpc_pipe_t pipe
= xpc_pipe_create_from_port(port
, 0);
198 xpc_object_t message
= xpc_dictionary_create(NULL
, NULL
, 0);
199 xpc_dictionary_set_int64(message
, "child_pid", getpid());
200 CHECK_EQ(0, xpc_pipe_simpleroutine(pipe
, message
));
202 xpc_release(message
);
208 XPC_TEST_F(ForwardMessage
) // {
210 XPCMessageServer
* first_server
= first
.server();
213 XPCMessageServer
* second_server
= second
.server();
215 ASSERT_TRUE(first
.Initialize(^(IPCMessage request
) {
216 xpc_dictionary_set_int64(request
.xpc
, "seen_by_first", 1);
217 first_server
->ForwardMessage(request
, second_server
->GetServerPort());
219 ASSERT_TRUE(second
.Initialize(^(IPCMessage request
) {
220 IPCMessage reply
= second_server
->CreateReply(request
);
221 xpc_dictionary_set_int64(reply
.xpc
, "seen_by_first",
222 xpc_dictionary_get_int64(request
.xpc
, "seen_by_first"));
223 xpc_dictionary_set_int64(reply
.xpc
, "seen_by_second", 2);
224 second_server
->SendReply(reply
);
227 xpc_object_t request
= xpc_dictionary_create(NULL
, NULL
, 0);
229 ASSERT_EQ(0, xpc_pipe_routine(first
.pipe(), request
, &reply
));
231 EXPECT_EQ(1, xpc_dictionary_get_int64(reply
, "seen_by_first"));
232 EXPECT_EQ(2, xpc_dictionary_get_int64(reply
, "seen_by_second"));
234 xpc_release(request
);
238 } // namespace sandbox